Rust能和其他编程语言一样从控制台获得用户输入,但Rust的处理方式确和其他语言有着很大的区别,这其中有着很多值得学习和思考的东西。
从一个小例子开始:
let mut buf = String::new();
io::stdin().read_line(&mut buf);
Stdin是一个结构体,并实现了一个叫read_line()的方法,这个方法是这个样子的:
它的工作就是从控制台中读取一行用户输入,并写入buf中,编译一下,并没有出错,而是给出了一个警告:
warning: unused result which must be used, #[warn(unused_must_use)] on by default
由此可见我们的工作并没有完成,因为read_line()方法并没有直接返回读取的长度,而是用Rusult将这个长度值包裹起来了,这是为什么呢?
因为从安全的角度来说,从控制台中获取输入,可能成功也可以失败,而read_line()的返回值Result就包含了成功和失败的两种可能,必须要对其处理,先来看看Result的结构:(https://doc.rust-lang.org/stable/std/result/enum.Result.html)
enum Result {
Ok(T),
Err(E)
}
正确的结果会被包含在Ok中,出错则返回Err,Result已经实现了相关判断函数比如is_ok(),is_err(),这里强调一下ok()和err()方法,看手册:
从文档中可以看出来,如果Result的结果是正确的,那么就将它转换成Option::Some,如果它是错误的,就将它转换成Option::None,经过ok()的转换以后,前面的处理中对和错的结果就被转换成了期待的数据和空两种形式,在上面的小例子中,如果没有出错,数据就会存到buf中去,如果失败了呢?那就只能提示出错了,下面来看看,在这最后一步的错误处理中如何操作。
在前面的说明中ok()方法返回了一个Option,在这个例子中,我们只需要处理Option::None就可以了,也是就是说,如果这里为None的话,那么buf里也就什么也没有获得,提示出错就好了。
先来看看Option这个enum (参照文档:https://doc.rust-lang.org/stable/std/option/enum.Option.html)
enum Option {
None,
Some(T),
}
它呢,和Result差不多,而我们用到的是一个它已经实现的方法,叫做:expect(),文档中声明如下:
也就是说,它会判断这个Option是Some还是None,如果是Some,它就会取出其中的T并返回,如果遇到了None,那么就出错,并将参数中的字符串作为错误消息打印出来。
这个时候,来个完成的程序,总结一下:
use std::io;
fn main() {
let mut buf = String::new();
println!("Please input your name:"); // 这行输出需要包含一个换行符,否则要等到你输入完后才能看见
io::stdin().read_line(&mut buf).ok().expect("Error!");
println!("Hello {} !", buf);
}
额~输出是这样的:
! 怎么跑到下面去了,原因是:
原因是我们刚才的输入中包含了一个回车符,Rust连同这个换行符一并放到了buf中,这显然不是我们想要的,所以我们在这里应该处理一下这个回车符,接着上面的代码:
let name = buf.trim();
println!("Hello {} !", name);
trim方法很简单,和大多数其他语言中的trim函数一样,就是去掉字符串两边多余的字符,参见
https://doc.rust-lang.org/stable/std/string/struct.String.html#method.trim
整理一下:
use std::io;
fn main() {
let mut buf = String::new();
println!("Please input your name:");
io::stdin().read_line(&mut buf).ok().expect("Error!");
let name = buf.trim();
println!("Hello {} !", name);
}
从上面的代码看到,我们自始至终都没有使用类似golang中到处都是if err的处理方式,所有的操作在一行内统统完成,是不是非常优雅,在这其中Result和Option扮演了非常重要的角色,在Rust有许许多多这样的使用方式,需要好好理解这两个enum的作用。