尝试转换

? 的有效展开比前面介绍的内容略微复杂一些:

1. expression?

效果等同于

1. match expression {
2. Ok(value) => value,
3. Err(err)  => return Err(From::from(err)),
4. }

The From::from call here means we attempt to convert the error type to the type returned by the function. This makes it easy to encapsulate errors into higher-level errors.

示例

1. use std::error::Error;
2. use std::fmt::{self, Display, Formatter};
3. use std::fs::File;
4. use std::io::{self, Read};

6. #[derive(Debug)]
7. enum ReadUsernameError {
8. IoError(io::Error),
9. EmptyUsername(String),
10. }

12. impl Error for ReadUsernameError {}

14. impl Display for ReadUsernameError {
15. fn fmt(&self, f: &mut Formatter) -> fmt::Result {
16. match self {
17. Self::IoError(e) => write!(f, "IO error: {e}"),
18. Self::EmptyUsername(path) => write!(f, "Found no username in {path}"),
19. }
20. }
21. }

23. impl From<io::Error> for ReadUsernameError {
24. fn from(err: io::Error) -> Self {
25. Self::IoError(err)
26. }
27. }

29. fn read_username(path: &str) -> Result<String, ReadUsernameError> {
30. let mut username = String::with_capacity(100);
31. File::open(path)?.read_to_string(&mut username)?;
32. if username.is_empty() {
33. return Err(ReadUsernameError::EmptyUsername(String::from(path)));
34. }
35. Ok(username)
36. }

38. fn main() {
39. //fs::write("config.dat", "").unwrap();
40. let username = read_username("config.dat");
41. println!("username or error: {username:?}");
42. }

This slide should take about 5 minutes.

The ? operator must return a value compatible with the return type of the function. For Result, it means that the error types have to be compatible. A function that returns Result<T, ErrorOuter> can only use ? on a value of type Result<U, ErrorInner> if ErrorOuter and ErrorInner are the same type or if ErrorOuter implements From<ErrorInner>.

From 实现的常见替代方案是 Result::map_err,尤其是只在一个位置进行转换时。

There is no compatibility requirement for Option. A function returning Option<T> can use the ? operator on Option<U> for arbitrary T and U types.

A function that returns Result cannot use ? on Option and vice versa. However, Option::ok_or converts Option to Result whereas Result::ok turns Result into Option.