Можно ли написать этот код 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 просто проверяет тип, чтобы увидеть, какой подтип был возвращен. Я не стал бы слишком зацикливаться на этой параллели, хотя было бы плохой идеей так стараться сопоставить понятия между языками.