Я все еще новичок в Rust и пишу инструмент CLI, который считывает ввод пользователя и выполняет вызовы API или локальные запросы к БД через оператор соответствия. Ошибка, с которой я сталкиваюсь, находится в строке "new" => util::db::insert_number(&conn, user_input).await Я могу вызывать функции без возвращаемого типа (banner() и desc()) и возвращаемого типа Result вне операторов соответствия, но не внутри. Может ли кто-нибудь поставить меня на правильный путь, чтобы решить эту проблему.
TLDR: оператор match ожидает возвращаемый тип (), но я хочу вызывать функции с возвращаемым типом Result<()>
ОШИБКА:
expected unit type `()` found enum `Result<(), rusqlite::Error>
cli.rs/main_loop():
pub async fn main_loop() -> Result<()> {
banner();
desc();
let conn = Connection::open("db.db").expect("connection failed");
util::db::check_db(&conn).await;
let mut user_input: Vec<String>;
let mut rl = Editor::<()>::new();
if rl.load_history(".history").is_err() {
println!("no previous history...");
}
println!("\t\t type 'new <number>' to add a number to the db");
println!("\t\t type 'exit' to leave configuration mode\n");
loop {
let readline = rl.readline("CONFIG# ");
match readline {
Ok(line) => {
user_input = get_string_vec(line);
match user_input[0].as_str() {
"new" => util::db::insert_number(&conn, user_input).await,
"exit" => break,
_ => continue,
}
},
Err(ReadlineError::Interrupted) => {
println!("ctrl+c pressed. quitting now..");
std::process::exit(0);
},
Err(ReadlineError::Eof) => {
println!("ctrl+d pressed. quitting now..");
std::process::exit(0);
},
Err(err) => {
println!("error: {:?}", err);
std::process::exit(0);
}
}
}
Ok(())
}
db.rs/insert_number():
pub async fn insert_number(conn: &Connection, args: Vec<String>) -> Result<()> {
//let conn = Connection::open("db.db").expect("connection failed");
conn.execute(
"insert into numbers (number) values (?1)",
&[args[1].as_str()],
).expect("insert failed");
Ok(())
}
Я попытался удалить все случаи, кроме случая, который возвращает Result<()>

Давайте удалим из вашего кода все, что является шумом, чтобы понять, откуда берется ошибка (не обращайте внимания на то, что имена некоторых переменных теперь не определены):
loop {
match readline {
Ok(line) => {
match user_input[0].as_str() {
"new" => insert_number(&conn, user_input).await,
_ => continue,
}
},
_ => {}
}
}
Когда Rust попытается проверить это, помимо прочего, он применит правило, согласно которому все ветки оператора/выражения соответствия должны иметь один и тот же тип, который является общим типом выражения соответствия. В частности, если вы примените это правило к внешнему совпадению, вы получите, что внутреннее совпадение должно иметь тип (). Затем, снова применив это правило, вы получите, что util::db::insert_number(&conn, user_input).await должен иметь тип (). Таким образом ошибка. Это связано с тем, что первая ветвь внешнего совпадения возвращает значение внутреннего совпадения.
Таким образом, очень простое исправление просто
loop {
match readline {
Ok(line) => {
match user_input[0].as_str() {
"new" => insert_number(&conn, user_input).await,
_ => continue,
};
// ^
},
_ => {}
}
}
Теперь мы добились того, чтобы первая ветвь внутреннего совпадения отбрасывала результат внутреннего совпадения и возвращала (), добавляя точку с запятой. Точно так же вы могли бы обернуть вызов insert_number в drop(...), что означает «игнорировать это значение и вернуть ()» следующим образом:
loop {
match readline {
Ok(line) => {
match user_input[0].as_str() {
"new" => drop(insert_number(&conn, user_input).await),
// ^^^^^ ^
_ => continue,
}
},
_ => {}
}
}
Однако есть одна вещь, которую я не сказал. Этот код компилируется, но выдается предупреждение: warning: unused `Result` that must be used. Компилятор сообщает вам, что вы выбросили значение типа Result, что в большинстве случаев не то, что вы хотели сделать.
Это связано с тем, что тип Result в Rust соответствует распространению ошибок. Игнорирование значения типа Result означает возможное игнорирование ошибки, что, вероятно, не то, что вы хотите сделать. Вместо этого Rust предполагает, что вы хотите как-то обработать эту ошибку.
Самый простой способ справиться с ошибкой — просто вызвать сбой программы, если произойдет ошибка. Это можно сделать с помощью .unwrap()
loop {
match readline {
Ok(line) => {
match user_input[0].as_str() {
"new" => insert_number(&conn, user_input).await.unwrap(),
// ^^^^^^^^^
_ => continue,
}
},
_ => {}
}
}
Аккуратно, теперь он компилируется и не выдает никаких предупреждений. Но... вы заметили, как эта функция вежливо дала вам возможность обработать ошибку, вернув Result<(), ...> вместо того, чтобы просто вылететь, если она сама достигла ошибки. Скорее всего, вызывающая сторона функции, которую вы пишете, также ожидает, что ваша функция будет такой же красивой. Действительно, ваша функция также имеет возвращаемый тип Result<(), ...> (обратите внимание, что Result<()> — это псевдоним для Result<(), T> с некоторым предопределенным T). Это будет «распространение ошибки»: пусть вызывающая сторона обрабатывает ошибку или передает ее тому, кто действительно хочет обработать ошибку. Это можно сделать довольно легко:
loop {
match readline {
Ok(line) => {
match user_input[0].as_str() {
"new" => match insert_number(&conn, user_input).await {
Ok(()) => (),
Err(err) => return Err(err),
},
_ => continue,
}
},
_ => {}
}
}
Теперь это (вероятно) работает. Я говорю «вероятно», потому что, возможно, вам нужно преобразовать return Err(err) в return Err(err.into()), в зависимости от фактических типов ошибок. Это просто и элегантно, но при этом очень многословно: я добавил три строки только для того, чтобы передать ошибку одного выражения. Было бы удобно, если бы был какой-то короткий макрос, который делал именно это за меня, верно...
loop {
match readline {
Ok(line) => {
match user_input[0].as_str() {
"new" => insert_number(&conn, user_input).await?,
// ^
_ => continue,
}
},
_ => {}
}
}
Да, ? делает именно это.
Спасибо за такое подробное объяснение. Я полностью понимаю ошибку в моем коде, и ваш ответ многому меня научил.
Возможно, вы имели в виду оператор try (
?) послеutil::db::insert_number(&conn, user_input).await