Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

result-panic/result #241

giscus[bot] bot announced in Book Comments
May 16, 2022 · 24 comments · 12 replies
Discussion options

result-panic/result

Learning Rust By Practice, narrowing the gap between beginner and skilled-dev with challenging examples, exercises and projects.

https://zh.practice.rs/result-panic/result.html

You must be logged in to vote

Replies: 24 comments 12 replies

Comment options

第一题的题目和答案对不上

You must be logged in to vote
1 reply
Comment options

感觉是我理解错了,我以为只能填写横线,不能改动其它地方

Comment options

有大佬解惑吗?这里为什么不能用两个map,前面必须用and_then会报错。

// 提示:使用 and_thenmap
fn multiply1(n1_str: &str, n2_str: &str) -> Result<i32, ParseIntError> {
n1_str.parse::().map(|x|n2_str.parse::().map(|y|{x*y}))
}

You must be logged in to vote
6 replies
Comment options

原因是map使用的闭包函数的返回类型不是Result,第二个map不适合放到第一个map的闭包函数里。

pub fn and_then<U, F>(self, op: F) -> Result<U, E>
where
F: FnOnce(T) -> Result<U, E>,

pub fn map<U, F>(self, op: F) -> Result<U, E>
where
F: FnOnce(T) -> U,

Comment options

fn multiply(n1_str: &str, n2_str: &str) -> Result<i32, ParseIntError> {

let n1 = n1_str.parse::<i32>();
let n2 = n2_str.parse::<i32>();
n1.and_then(|x| n2.map(|y| y * x))

}

Comment options

本题中函数multiply返回的类型是Result<i32, ParseIntError>,
如果用两个map那么返回值就变成了Result< Result<i32, ParseIntError>, ParseIntError>了.

嵌套的map 会返回一个 Result<i32, ParseIntError>, 此时会变成外层的map返回的Result的Ok值

Comment options

pub fn map<U, F: FnOnce(T) -> U>(self, op: F) -> Result<U, E> {
match self {
Ok(t) => Ok(op(t)),
Err(e) => Err(e),
}
}

pub fn and_then<U, F: FnOnce(T) -> Result<U, E>>(self, op: F) -> Result<U, E> {
match self {
Ok(t) => op(t),
Err(e) => Err(e),
}
}

Comment options

因为map要求的闭包必须返回的是一个T类型,在这里是返回i32类型,但map本身又是返回的Result类型;所以两个map嵌套用的话,内嵌的map的返回类型没法满足外层map对所需闭包的要求。

Comment options

5题另一种实现:

Ok(n1_str.parse::<i32>()? * n2_str.parse::<i32>()?)

You must be logged in to vote
0 replies
Comment options

第5题:我是这么写的,这块东西还没讲到
type Res = Result<T, ParseIntError>;

You must be logged in to vote
0 replies
Comment options

main里的return问题
原题如果改成用特征对象就会报错

use std::num::ParseIntError;
fn main() -> Result<(), Box<dyn std::error::Error>> {
 let number_str = "10";
 let number = match number_str.parse::<i32>() {
 Ok(number) => number,
 Err(e) => return Err(e),
 };
 println!("{}", number);
 Ok(())
}

?就成功,这是为什么呢?不是说?是个宏,其实也是return了Err(e)的吗?

use std::num::ParseIntError;
fn main() -> Result<(), Box<dyn std::error::Error>> {
 let number_str = "10";
 let number = number_str.parse::<i32>()?;
 println!("{}", number);
 Ok(())
}
You must be logged in to vote
1 reply
Comment options

Ok 犯蠢了

Err(e) => return Err(Box::new(e)), 就可以了

Comment options

mark

You must be logged in to vote
0 replies
Comment options

第一题:

// 填空并修复错误
use std::num::ParseIntError;
fn multiply(n1_str: &str, n2_str: &str) -> Result<i32, ParseIntError> {
 let n1 = n1_str.parse::<i32>();
 let n2 = n2_str.parse::<i32>();
 Ok(n1.unwrap() * n2.unwrap())
}
fn main() {
 let result = multiply("10", "2");
 assert_eq!(result, Ok(20));
 let result = multiply("4", "2");
 assert_eq!(result.unwrap(), 8);
 println!("Success!")
}

第二题:

use std::num::ParseIntError;
// 使用 `?` 来实现 multiply
// 不要使用 unwrap !
fn multiply(n1_str: &str, n2_str: &str) -> Result<i32, ParseIntError> {
 Ok(n1_str.parse::<i32>()? * n2_str.parse::<i32>()?)
}
fn main() {
 assert_eq!(multiply("3", "4").unwrap(), 12);
 println!("Success!")
}

第三题:

use std::fs::File;
use std::io::{self, Read};
fn read_file1() -> Result<String, io::Error> {
 let f = File::open("hello.txt");
 let mut f = match f {
 Ok(file) => file,
 Err(e) => return Err(e),
 };
 let mut s = String::new();
 match f.read_to_string(&mut s) {
 Ok(_) => Ok(s),
 Err(e) => Err(e),
 }
}
// 填空
// 不要修改其它代码
fn read_file2() -> Result<String, io::Error> {
 let mut s = String::new();
 File::open("hello.txt")?.read_to_string(&mut s)?;
 Ok(s)
}
fn main() {
 assert_eq!(read_file1().unwrap_err().to_string(), read_file2().unwrap_err().to_string());
 println!("Success!")
}

第四题:

use std::num::ParseIntError;
// 使用两种方式填空: map, and then
fn add_two1(n_str: &str) -> Result<i32, ParseIntError> {
 n_str.parse::<i32>().map(|n| n + 2)
}
fn add_two2(n_str: &str) -> Result<i32, ParseIntError> {
 n_str.parse::<i32>().and_then(|n| Ok(n + 2))
 }
fn main() {
 assert_eq!(add_two1("4").unwrap(), 6);
 assert_eq!(add_two2("4").unwrap(), 6);
 println!("Success!")
}

第五题:

use std::num::ParseIntError;
// 使用 Result 重写后,我们使用模式匹配的方式来处理,而无需使用 `unwrap`
// 但是这种写法实在过于啰嗦..
fn multiply(n1_str: &str, n2_str: &str) -> Result<i32, ParseIntError> {
 match n1_str.parse::<i32>() {
 Ok(n1) => {
 match n2_str.parse::<i32>() {
 Ok(n2) => {
 Ok(n1 * n2)
 },
 Err(e) => Err(e),
 }
 },
 Err(e) => Err(e),
 }
}
// 重写上面的 `multiply` ,让它尽量简介
// 提示:使用 `and_then` 和 `map`
fn multiply1(n1_str: &str, n2_str: &str) -> Result<i32, ParseIntError> {
 // 实现...
 Ok(n1_str.parse::<i32>()? * n2_str.parse::<i32>()?)
}
fn print(result: Result<i32, ParseIntError>) {
 match result {
 Ok(n) => println!("n is {}", n),
 Err(e) => println!("Error: {}", e),
 }
}
fn main() {
 let twenty = multiply1("10", "2");
 print(twenty);
 // 下面的调用会提供更有帮助的错误信息
 let tt = multiply("t", "2");
 print(tt);
 println!("Success!")
}

第六题:

use std::num::ParseIntError;
// 填空
type Res<T> = Result<T, ParseIntError>;
// 使用上面的别名来引用原来的 `Result` 类型
fn multiply(first_number_str: &str, second_number_str: &str) -> Res<i32> {
 first_number_str.parse::<i32>().and_then(|first_number| {
 second_number_str.parse::<i32>().map(|second_number| first_number * second_number)
 })
}
// 同样, 这里也使用了类型别名来简化代码
fn print(result: Res<i32>) {
 match result {
 Ok(n) => println!("n is {}", n),
 Err(e) => println!("Error: {}", e),
 }
}
fn main() {
 print(multiply("10", "2"));
 print(multiply("t", "2"));
 println!("Success!")
}
You must be logged in to vote
1 reply
Comment options

第五题要求使用and_then和map,修改如下:

use std::num::ParseIntError;
// 使用 Result 重写后,我们使用模式匹配的方式来处理,而无需使用 `unwrap`
// 但是这种写法实在过于啰嗦..
fn multiply(n1_str: &str, n2_str: &str) -> Result<i32, ParseIntError> {
 match n1_str.parse::<i32>() {
 Ok(n1) => {
 match n2_str.parse::<i32>() {
 Ok(n2) => {
 Ok(n1 * n2)
 },
 Err(e) => Err(e),
 }
 },
 Err(e) => Err(e),
 }
}
// 重写上面的 `multiply` ,让它尽量简介
// 提示:使用 `and_then` 和 `map`
fn multiply1(n1_str: &str, n2_str: &str) -> Result<i32, ParseIntError> {
 // 实现...
 n1_str.parse::<i32>().and_then(|n1| {
 n2_str.parse::<i32>().map(|n2| n1 * n2)
 })
}
fn print(result: Result<i32, ParseIntError>) {
 match result {
 Ok(n) => println!("n is {}", n),
 Err(e) => println!("Error: {}", e),
 }
}
fn main() {
 let twenty = multiply1("10", "2");
 print(twenty);
 // 下面的调用会提供更有帮助的错误信息
 let tt = multiply("t", "2");
 print(tt);
 println!("Success!")
}
Comment options

lala~

You must be logged in to vote
0 replies
Comment options

前3题,关注Result?
1、Result<T,E>返回需要声明一下具体的返回类型

// 填空并修复错误
use std::num::ParseIntError;
fn multiply(n1_str: &str, n2_str: &str) -> Result<i32, ParseIntError> {
 let n1 = n1_str.parse::<i32>();
 let n2 = n2_str.parse::<i32>();
 Ok(n1.unwrap() * n2.unwrap())
}
fn main() {
 let result = multiply("10", "2");
 assert_eq!(result, Ok(20));
 let result = multiply("4", "2");
 assert_eq!(result.unwrap(), 8);
 println!("Success!")
}

2、?的使用

use std::num::ParseIntError;
// 使用 `?` 来实现 multiply
// 不要使用 unwrap !
fn multiply(n1_str: &str, n2_str: &str) -> Result<i32, ParseIntError>{
 let n1 = n1_str.parse::<i32>();
 let n2 = n2_str.parse::<i32>();
 Ok(n1?*n2?)
}
fn main() {
 assert_eq!(multiply("3", "4").unwrap(), 12);
 println!("Success!")
}

3、?的链式使用

use std::fs::File;
use std::io::{self, Read};
fn read_file1() -> Result<String, io::Error> {
 let f = File::open("hello.txt");
 let mut f = match f {
 Ok(file) => file,
 Err(e) => return Err(e),
 };
 let mut s = String::new();
 match f.read_to_string(&mut s) {
 Ok(_) => Ok(s),
 Err(e) => Err(e),
 }
}
// 填空
// 不要修改其它代码
fn read_file2() -> Result<String, io::Error> {
 let mut s = String::new();
 File::open("hello.txt")?.read_to_string(&mut s)?;
 Ok(s)
}
fn main() {
 assert_eq!(read_file1().unwrap_err().to_string(), read_file2().unwrap_err().to_string());
 println!("Success!")
}
You must be logged in to vote
0 replies
Comment options

use std::num::ParseIntError;
// 使用 `?` 来实现 multiply
// 不要使用 unwrap !
fn multiply(n1_str: &str, n2_str: &str) -> Result<i32, ParseIntError> {
 Ok(n1_str.parse::<i32>()? * n2_str.parse::<i32>()?)
}
fn main() {
 assert_eq!(multiply("3", "4").unwrap(), 12);
 println!("Success!")
}
You must be logged in to vote
0 replies
Comment options

如果我的函数中可能会出现两个以上不同类型的Error,怎么使用Result啊

You must be logged in to vote
1 reply
Comment options

文章有讲呀,使用动态对象,Box<dyn std::error::Error> 它可以接受所有类型错误

Comment options

第五题
let n2=n2_str.parse::<i32>()?;
n1_str.parse::<i32>().map(|x| x*n2)
 
let n2=n2_str.parse::<i32>()?;
n1_str.parse::<i32>().and_then(|x| Ok(x*n2))
 
 let n1=n1_str.parse::<i32>()?;
 let n2=n2_str.parse::<i32>()?;
 Ok(n1*n2)
You must be logged in to vote
0 replies
Comment options

map 和 and then什么时候讲的 闭包参数不是到后面才有吗

You must be logged in to vote
0 replies
Comment options

第五题这样写也能通过
fn multiply1(n1_str: &str, n2_str: &str) -> Result<i32, ParseIntError> {
n1_str.parse::().map(|n1| {
n2_str.parse::().and_then(|n2| Ok(n1*n2))})?
}

You must be logged in to vote
1 reply
Comment options

两个map也行
fn multiply1(n1_str: &str, n2_str: &str) -> Result<i32, ParseIntError> {
n1_str.parse::().map(|n1| {
n2_str.parse::().map(|n2| n1*n2)})?
}

Comment options

如果有人知道 Haskell 的话,Rust 的 map 就是 Haskell 的 fmap,而 and_then 就是 Haskell 的 >>=。

fmap :: Functor f => f a -> (a -> b) -> f b
(>>=) :: Monad m => m a -> (a -> m b) -> m b

对应到 Rust 就是:
fmap[map] :
输入

  • Result<i32, E>
  • i32 -> f32
    输出
  • Result<f32, E>

(>>=)[and_then]:
输入

  • Result<i32, E>
  • i32 -> Result<f32, E>
    输出
  • Result<f32, E>
You must be logged in to vote
0 replies
Comment options

第五题可以用一种更加 Haskell 式(或者 Async/Await 式)的方法来写

use std::num::ParseIntError;
use donotation::m;
// 使用 Result 重写后,我们使用模式匹配的方式来处理,而无需使用 `unwrap`
// 但是这种写法实在过于啰嗦..
fn multiply(n1_str: &str, n2_str: &str) -> Result<i32, ParseIntError> {
 match n1_str.parse::<i32>() {
 Ok(n1) => {
 match n2_str.parse::<i32>() {
 Ok(n2) => {
 Ok(n1 * n2)
 },
 Err(e) => Err(e),
 }
 },
 Err(e) => Err(e),
 }
}
// 重写上面的 `multiply` ,让它尽量简洁
// 提示:使用 `and_then` 和 `map`
fn multiply1(n1_str: &str, n2_str: &str) -> Result<i32, ParseIntError> {
 m! {
 x <- n1_str.parse::<i32>();
 y <- n2_str.parse::<i32>();
 return x * y;
 }
}
fn print(result: Result<i32, ParseIntError>) {
 match result {
 Ok(n) => println!("n is {}", n),
 Err(e) => println!("Error: {}", e),
 }
}
fn main() {
 let twenty = multiply1("10", "2");
 print(twenty);
 // 下面的调用会提供更有帮助的错误信息
 let tt = multiply("t", "2");
 print(tt);
 println!("Success!")
}
You must be logged in to vote
1 reply
Comment options

不过作者好像没有 donotation::m 这个包,因此我们只能把 m! 宏的源码复制过来:

macro_rules! m {
 // return
 (return $r:expr ;) => {
 $crate::Lift::lift($r)
 };
 // let-binding
 (let $p:pat = $e:expr ; $($r:tt)*) => {{
 let $p = $e;
 m!($($r)*)
 }};
 // const-bind
 (_ <- $x:expr ; $($r:tt)*) => {
 $x.and_then(move |_| { m!($($r)*) })
 };
 // bind
 ($binding:ident <- $x:expr ; $($r:tt)*) => {
 $x.and_then(move |$binding| { m!($($r)*) })
 };
 // const-bind
 ($e:expr ; $($a:tt)*) => {
 $e.and_then(move |_| m!($($a)*))
 };
 // pure
 ($a:expr) => {
 $a
 }
}
/// Lift a value inside a monad.
pub trait Lift<A> {
 /// Lift a value into a default structure.
 fn lift(a: A) -> Self;
}
impl<A> Lift<A> for Option<A> {
 fn lift(a: A) -> Self {
 Some(a)
 }
}
impl<A, E> Lift<A> for Result<A, E> {
 fn lift(a: A) -> Self {
 Ok(a)
 }
}

用以上源码代替 use donotation::m 即可。

Comment options

done!

You must be logged in to vote
0 replies
Comment options

第五题的四种写法:

// fn multiply1(n1_str: &str, n2_str: &str) -> Result<i32, ParseIntError> {
// // 实现...
// n1_str.parse::<i32>().map(|n1| n1 * n2_str.parse::<i32>().unwrap())
// }
// fn multiply1(n1_str: &str, n2_str: &str) -> Result<i32, ParseIntError> {
// n1_str.parse::<i32>().map(|n1| {
// n2_str.parse::<i32>().map(|n2| n2 * n1)
// })? // 不加?的类型: Result<Result<i32, ParseIntError>, ParseIntError>
// }
// fn multiply1(n1_str: &str, n2_str: &str) -> Result<i32, ParseIntError> {
// n1_str.parse::<i32>().map(|n1| {
// n2_str.parse::<i32>().and_then(|n2| Ok(n2 * n1))
// })? // 不加?的类型: Result<Result<i32, ParseIntError>, ParseIntError> 
// }
fn multiply1(n1_str: &str, n2_str: &str) -> Result<i32, ParseIntError> {
 n1_str.parse::<i32>().and_then(|n1| {
 n2_str.parse::<i32>().map(|n2| n2 * n1)
 }) 
}
You must be logged in to vote
0 replies
Comment options

done~~~

You must be logged in to vote
0 replies
Comment options

mark finished

You must be logged in to vote
0 replies
Comment options

第3题不对吧,如果有 hello.txt 文件,在main函数中unwrap_err 不就 panic 了吗?"Success!" 不能

You must be logged in to vote
0 replies
Comment options

一个问题,这里的?宏运算,我直接这样返回也是可以的,为啥还需要?运算,有咩有一个好的例子说明传播错误的重要性,以及?在这里面的作用。

fn divide(x: f64, y: f64) -> Result<f64, &'static str> {
 if y == 0.0 {
 Err("除数不能为0")
 } else {
 Ok(x / y)
 }
}
fn calculate(x: f64, y: f64, z: f64) -> Result<f64, &'static str> {
 let result1 = divide(x, y)?;
//在这里下面的两行,我直接返回divide(result1, z)调用结果也是可以的,为什么还需要多此一举?
 let result2=divide(result1, z)?
 result2
}
fn main() {
 let result = calculate(10.0, 2.0, 0.0);
 match result {
 Ok(val) => println!("计算结果: {}", val),
 Err(err) => println!("出错了: {}", err),
 }
}
You must be logged in to vote
0 replies
Comment options

第一题

// 填空并修复错误
use std::num::ParseIntError;
fn multiply(n1_str: &str, n2_str: &str) -> Result<i32, ParseIntError> {
 let n1 = n1_str.parse::<i32>();
 let n2 = n2_str.parse::<i32>();
 Ok(n1? * n2?)
}
fn main() {
 let result = multiply("10", "2");
 assert_eq!(result, Ok(20));
 let result = multiply("t", "2");
 assert_eq!(result.unwrap_or(8), 8);
 println!("Success!")
}

第二题

fn multiply(n1_str: &str, n2_str: &str) -> Result<i32, ParseIntError> {
 let n1 = n1_str.parse::<i32>();
 let n2 = n2_str.parse::<i32>();
 Ok(n1? * n2?)
}

第三题

fn read_file2() -> Result<String, io::Error> {
 let mut s = String::new();
 File::open("hello.txt")?.read_to_string(&mut s)?;
 Ok(s)
}

第四题

fn add_two(n_str: &str) -> Result<i32, ParseIntError> {
 // n_str.parse::<i32>().map(|n| n + 2)
 n_str.parse::<i32>().and_then(|n| Ok(n + 2))
}

第五题

fn multiply1(n1_str: &str, n2_str: &str) -> Result<i32, ParseIntError> {
 // 实现1...
 /*let result = n1_str.parse::<i32>()? * n2_str.parse::<i32>()?;
 Ok(result)*/
 
 // 实现2...
 n1_str.parse::<i32>().and_then(|n1| n2_str.parse::<i32>().map(|n2|n1 * n2))
}

第六题

type Res<T> = Result<T, ParseIntError>;
You must be logged in to vote
0 replies
Comment options

except the map& and_then, done

You must be logged in to vote
0 replies
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

AltStyle によって変換されたページ (->オリジナル) /