Можно ли написать этот код Rust без оператора match?

linuxfood создал привязки для sqlite3, за что я благодарен. Я только начинаю изучать Rust (0.8) и пытаюсь точно понять, что делает этот фрагмент кода:

extern mod sqlite;

fn db() {

    let database =
        match sqlite::open("test.db") {
            Ok(db) => db,
            Err(e) => {
                println(fmt!("Error opening test.db: %?", e));
                return;
            }
        };

Я понимаю, в основном, что он делает. Он пытается получить соединение с базой данных, а также проверяет наличие ошибки. Я не понимаю, как именно это происходит.

Чтобы лучше понять это, я хотел переписать это без match заявление, но у меня нет знаний, чтобы сделать это. Это возможно? Есть ли sqlite::open() вернуть две переменные или только одну?

Как этот пример может быть написан по-другому без match заявление? Я не говорю, что это необходимо или предпочтительнее, однако это может помочь мне выучить язык.

4 ответа

Решение

Внешний оператор является присваиванием, которое присваивает значение выражения соответствия database, Выражение соответствия зависит от возвращаемого значения sqlite::openкоторый, вероятно, имеет тип Result<T, E> (перечисление с вариантами Ok(T) а также Err(E)). Если это Ok, вариант enum имеет параметр, который выражение соответствия разрушает в db и возвращает это значение (поэтому оно присваивается переменной database). Если это Err, вариант enum имеет параметр с объектом ошибки, который печатается, и функция возвращает.

Без использования оператора сравнения это можно записать следующим образом (просто потому, что вы явно просили не использовать совпадение - большинство людей сочтут этот плохой стиль кодирования):

let res = sqlite::open("test.db");
if res.is_err() {
    println!("Error opening test.db: {:?}", res.unwrap_err());
    return;
}
let database = res.unwrap();

Я просто изучаю Rust самостоятельно, но это еще один способ справиться с этим.

if let Ok(database) = sqlite::open("test.db") {
    // Handle success case
} else {
    // Handle error case
}

Смотрите документацию оif let,

Эта функция open возвращаетсяSqliteResult<Database>; учитывая определениеpub type SqliteResult<T> = Result<T, ResultCode>, то есть std::result::Result<Database, ResultCode>,

Result является перечислением, и вы принципиально не можете получить доступ к вариантам перечисления без сопоставления: это, буквально, единственный путь. Конечно, у вас могут быть методы для абстрагирования соответствия, но они обязательно реализуются с помощью match.

Из документации Result видно, что в ней есть такие удобные методы, как is_errПримерно так (это не совсем так, но достаточно близко):

fn is_err(&self) -> bool {
    match *self {
        Ok(_) => false,
        Err(_) => true,
    }
}

а также unwrap (опять только приблизительный):

fn unwrap(self) -> T {
    match self {
        Ok(t) => t,
        Err(e) => fail!(),
    }
}

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

sqlite::open() возвращает Enum. Перечисления немного отличаются по ржавчине, к каждому значению перечисления могут быть прикреплены поля.
См. http://static.rust-lang.org/doc/0.8/tutorial.html

Так что в этом случае SqliteResult enum может быть Ok или же Err если это Ok тогда к нему прилагается ссылка на БД, если Err тогда это имеет подробности ошибки.

С C# или Java фоном вы могли бы рассмотреть SqliteResult в качестве базового класса, который Ok а также Err унаследовать, каждый со своей соответствующей информацией. В этом сценарии предложение match просто проверяет тип, чтобы увидеть, какой подтип был возвращен. Я не стал бы слишком зацикливаться на этой параллели, хотя было бы плохой идеей так стараться сопоставить понятия между языками.

Другие вопросы по тегам