Хотя ваши две версии кода семантически эквивалентны, для компилятора они на самом деле совершенно разные.
Отказавший вызов вызывает Result::map_err()
с замыканием, которое фиксирует значение client
. То есть client
перемещается в замыкание, но оно заимствуется при вызове check_approval()
. И здесь кроется ошибка, заимствованное значение не может быть перемещено.
Вы можете подумать, что этот заем должен завершиться sh, когда функция вернется, но это не так из-за ее типа возврата Result<&'a str, ()>
, будучи 'a
точно временем жизни этого заимствования. Займ client
продлевается до тех пор, пока существует 'a
. И именно поэтому ваша вторая версия работает: когда вы соответствуете Result
, 'a
не распространяется на Err(())
ветвь, только на Ok(&'a str)
, поэтому Err(())
может свободно перемещаться client
.
Есть ли способ обойти это и написать код без использования match
?
Ну, вы звоните authorized.to_string()
в возвращенном &'a str
и преобразование его в принадлежащий String
. Итак, если вы можете изменить ограничение CheckApproval
на:
CheckApproval: FnOnce(&Client, &str) -> Result<String, ()>,
, проблема просто исчезнет.
Если вы не можете это изменить, другой вариант - сделать to_string()
перед перемещение client
в закрытие, завершение заимствования до того, как он может причинить вред:
let authorized = check_approval(&client, permission)
.map(|a| a.to_string())
.map_err(|_| MyErr::RequiresApproval(client, permission.to_string()))?;
Ok(authorized)