use std::io;
fn main() {
println!("Guess the number!");
println!("Please input your guess.");
let mut guess = String::new();
io::stdin().read_line(&mut guess)
.ok()
.expect("Failed to read line");
println!("You guessed: {}", guess);
}
println!("Guess the number!");
println!("Please input your guess.");
我们先前学习了println!()是一个宏,该宏在屏幕上打印一个字符串。
let mut guess = String::new();
现在变的有趣了!在这一样中有许多东西。第一个需要注意的事情是,这是一个let声明,用来创建变量捆绑(variable bindings)。他们的格式如下:
let foo = bar;
这将会创建一个名为foo的新捆绑,将其绑定到值bar上。在很多语言中,这叫做变量(variable),但是Rust的变量捆绑有一些玄机。
例如,他们默认是不可变的。这就是为什么我们的例子使用mut:它是一个捆绑是可变的,而不是不可变的。在左边let并不会接受一个名字,实际上它接受一个‘模式(pattern)’。我们将会在后面使用模式。它使用简单:
let foo = 5; // immutable.
let mut bar = 5; // mutable
//将会开始一个注释,直到行尾。Rust会忽略注释中的所有东西。
现在我们知道let mut guess将会引入一个名为guess的可变绑定,但是我们需要看一下=的另一端,看看它绑定了什么:String::new()。
::new()语法使用::是因为这是一个特殊类型的关联函数(associated function)。也就是说,它关联到String自己,而不是String的一个特殊实例。有些函数称作为“静态方法”。
io::stdin().read_line(&mut guess)
.ok()
.expect("Failed to read line");
这写跟多!让我们一位一位的解读。第一行有两部分。这是第一部分:
io::stdin()
还记得我们在程序的第一行怎么使用std::io吗?我们现在调用它的一个关联函数。如果我们没有使用use std::io,我们可以这么书写这一行:std::io::stdin()。
下一部分竟会使用这个句柄获取用户输入:
read_line(&mut guess)
这里,我们在我们的句柄上调用read_line()方法。方法类似于关联函数,但是仅仅能使用在一个类型的特殊实例上,而不是类型本身。我们也给read_line()传递了一个参数:&mut guess。
还记得上面我们怎么捆绑guess的吗?我们说它是可变的。然而,read_line不会把一个String作为一个参数:它使用&mut String。Rust有一个特点叫做引用(reference),这允许你对一份数据有多分引用,这可以减少拷贝。引用是一个复杂的特点,作为Rust的一个主要卖点是怎样安全简单的使用引用。现在完成我们的程序我们并不需要知道过多的细节。现在,我们需要知道的是let捆绑、引用默认是不可变的。因此我们需要使用$mut guess,而不是&guess。
为什么read_line()使用一个字符串的一个可变引用?它的工作是获取用户输入到标准输入数据,并把数据放在一个字符串中。所以它将这个字符串作为一个参数,为了加入输入,需要可变。
但是我们还是不太明白这行代码。尽管这是一行文本,它仅仅是单行逻辑代码的第一部分:
.ok()
.expect("Failed to read line");
当你使用.foo()语法调用方法时,你可能会引入新的一行和空白字符。这能帮助你分开比较长的行。我们也可以使用:
io::stdin().read_line(&mut guess).ok().expect("failed to read line");
这是这使代买难以阅读。所以我们将其分开,每个方法调用占用一行。我们已经讨论过read_line()了,但是还有ok()和expect()呢?我们已经说过read_line()将用户的输入传递给&mut String。但是它会返回一个值:在这种情况下是一个io::Result。Rust在它的标准库中有一些名为Result的类型:一个通用的Result,和一个特殊版本的子库,例如io::Result。
这些Result类型的目的是编码错误处理信息。Result类型的值,跟任何类型一样,都有定义的方法。在这里,io::Result有一个ok()方法,意思为‘我们想假设这个值是一个正确的,如果不是,抛出错误信息即可’。为什么抛出?对于一个基础程序,我们仅仅想打印一个通用错误,通常意味着我们不能继续执行了。ok()方法返回一个值,有另外一个方法定义在该值上:expect()。expect()方法接受一个参数,如果不成功,将会停止(panic),并打印传递给它的信息。
如果我们不是用这两个方法,我们的程序可以正常编译,但是会有警告信息:
$ cargo build
Compiling guessing_game v0.1.0 (file:///home/you/projects/guessing_game)
src/main.rs:10:5: 10:39 warning: unused result which must be used,
#[warn(unused_must_use)] on by default
src/main.rs:10 io::stdin().read_line(&mut guess);
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Rust警告我们我们没有使用Result值。这个警告来自io::Result的一个特殊注解。Rust试图告诉你内没有处理一个潜在的错误。正确压制错的的方法是写一个错误处理程序。幸运的是,如果我们仅仅想在遇到问题是崩溃程序,我们可以使用这两个方法。如果我们能从错误中恢复,我们将做些额外的工作,但是我们将在未来的项目中保留这些。
在这个例子中还有一行代码:
println!("You guessed: {}", guess);
}
改代码输出我们保存输出的字符串。{}是占位符,所以我们将guess作为一个参数传递给它。如果我们有多个{},我们需要传递多个参数:
let x = 5;
let y = 10;
println!("x and y: {} and {}", x, y);
很简单。
我们可以使用cargo run运行我们的项目:
$ cargo run
Compiling guessing_game v0.1.0 (file:///home/you/projects/guessing_game)
Running `target/debug/guessing_game`
Guess the number!
Please input your guess.
6
You guessed: 6
好的!我们的第一部分完成了:我们可以从键盘读取输入,并将其打印出来。