Какая цель ErrorKind:: __ Отсутствует?

std::io::ErrorKind имеет вариант __Nonexhaustive. Я не знаю, в чем проблема, если этого варианта не существует.

Какая цель этого варианта?

Ответ 1

Он разработан, чтобы позволить расширять перечисление ErrorKind в будущем, заставляя любые операторы match в стабильном коде иметь захват _.

В частности, вариант отмечен неустойчивым, поэтому его нельзя ссылаться на стабильный канал, поэтому компилятор отклоняет код, например

fn foo(x: Error) {
    match x.kind() {
        ErrorKind::NotFound => {}
        ErrorKind::PermissionDenied => {}
        ErrorKind::ConnectionRefused => {}
        ErrorKind::ConnectionReset => {}
        ErrorKind::ConnectionAborted => {}
        ErrorKind::NotConnected => {}
        ErrorKind::AddrInUse => {}
        ErrorKind::AddrNotAvailable => {}
        ErrorKind::BrokenPipe => {}
        ErrorKind::AlreadyExists => {}
        ErrorKind::WouldBlock => {}
        ErrorKind::InvalidInput => {}
        ErrorKind::InvalidData => {}
        ErrorKind::TimedOut => {}
        ErrorKind::WriteZero => {}
        ErrorKind::Interrupted => {}
        ErrorKind::Other => {}
        ErrorKind::UnexpectedEof => {}
        ErrorKind::UnexpectedEOF => {}
        ErrorKind::__Nonexhaustive => {}
    }
}
<anon>:24:9: 24:35 error: use of unstable library feature 'io_error_internals': better expressed through extensible enums that this enum cannot be exhaustively matched against (see issue #0)
<anon>:24         ErrorKind::__Nonexhaustive => {}
                  ^~~~~~~~~~~~~~~~~~~~~~~~~~

Если этот код успешно скомпилирован на стабильной Rust, то добавление варианта к ErrorKind в будущей версии приведет к поломке любого кода, который имел match, как указано выше, а нарушение стабильного кода плохо. Код ломается, потому что совпадения в Rust должны быть исчерпывающими, то есть они должны каким-то образом покрывать каждую возможность, и, таким образом, добавление варианта означает, что возможность не покрывается.

Вместо этого программисты должны написать:

fn foo(x: Error) {
    match x.kind() {
        ErrorKind::NotFound => {}
        ErrorKind::PermissionDenied => {}
        ErrorKind::ConnectionRefused => {}
        ErrorKind::ConnectionReset => {}
        ErrorKind::ConnectionAborted => {}
        ErrorKind::NotConnected => {}
        ErrorKind::AddrInUse => {}
        ErrorKind::AddrNotAvailable => {}
        ErrorKind::BrokenPipe => {}
        ErrorKind::AlreadyExists => {}
        ErrorKind::WouldBlock => {}
        ErrorKind::InvalidInput => {}
        ErrorKind::InvalidData => {}
        ErrorKind::TimedOut => {}
        ErrorKind::WriteZero => {}
        ErrorKind::Interrupted => {}
        ErrorKind::Other => {}
        ErrorKind::UnexpectedEof => {}
        ErrorKind::UnexpectedEOF => {}
        _ => {}
    }
}

Это означает, что любые варианты, добавленные в ErrorKind в будущем (например, новые возможности ошибок для новых функций ввода-вывода), попадут под руку _, и, следовательно, существующий стабильный код не сломается.

Ответ 2

Цель этого скрытого варианта - помешать вам написать что-то вроде этого (которое не компилируется именно из-за наличия __Nonexhaustive):

use std::io::ErrorKind;

fn main() {
    let error_kind: ErrorKind = unimplemented!();
    match error_kind {
        ErrorKind::NotFound => unimplemented!(),
        ErrorKind::PermissionDenied => unimplemented!(),
        ErrorKind::ConnectionRefused => unimplemented!(),
        ErrorKind::ConnectionReset => unimplemented!(),
        ErrorKind::ConnectionAborted => unimplemented!(),
        ErrorKind::NotConnected => unimplemented!(),
        ErrorKind::AddrInUse => unimplemented!(),
        ErrorKind::AddrNotAvailable => unimplemented!(),
        ErrorKind::BrokenPipe => unimplemented!(),
        ErrorKind::AlreadyExists => unimplemented!(),
        ErrorKind::WouldBlock => unimplemented!(),
        ErrorKind::InvalidInput => unimplemented!(),
        ErrorKind::InvalidData => unimplemented!(),
        ErrorKind::TimedOut => unimplemented!(),
        ErrorKind::WriteZero => unimplemented!(),
        ErrorKind::Interrupted => unimplemented!(),
        ErrorKind::Other => unimplemented!(),
        ErrorKind::UnexpectedEOF => unimplemented!(),
        ErrorKind::UnexpectedEof => unimplemented!(),
        // note: no wildcard match arm here
    };
}

Причина, по которой разработчики стандартной библиотеки не хотят, чтобы вы это делали, заключается в сохранении возможности добавления вариантов в ErrorKind в будущем. Вариант __Nonexhaustive не позволяет вам выполнять исчерпывающее соответствие, просто обрабатывая каждый индивидуальный вариант; вы должны иметь подстановочный знак, чтобы иметь исчерпывающее соответствие.

В Rust выражение match требует, чтобы все возможные шаблоны для согласованного выражения имели соответствующий рычаг, так что выражение match всегда имеет четко определенное явное значение. A match, который охватывает все шаблоны, называется исчерпывающим. С enum s, Rust позволяет нам просто перечислить все варианты. Например, с Option, который имеет только 2 варианта, названных None и Some, мы можем написать:

fn main() {
    let option: Option<()> = unimplemented!();
    match option {
        None => unimplemented!(),
        Some(()) => unimplemented!(),
    };
}

Этот match компилируется отлично, потому что он охватывает все возможные шаблоны для Option. Однако, если тип Option получил другой вариант, то вдруг ваш код больше не компилируется, потому что он больше не будет исчерпывающим. Естественно, это не имеет смысла для Option, поэтому тип Option не играет в "неисчерпаемую" игру. Но если __Nonexhaustive не было, добавление варианта к ErrorKind было бы изменением; любой код, который сделал исчерпывающее соответствие (без подстановочных знаков) на ErrorKind, внезапно прекратил компиляцию. Этот код может быть в ящике, от которого зависит ваше приложение, и этот полом может помешать вам обновить Rust до тех пор, пока этот ящик не будет исправлен!