我认为学习计算机语言,应该先用后学,这一节,我们来实现一个猜数字的小游戏。



先简单介绍一个这个游戏的内容:游戏先生成一个1到100之间的任意一个数字,然后我们输入自己猜测的数字,游戏会告诉我们输入的数字太大还是太小,然后我们重新输入新的数字,直到猜到游戏生成的数字,然后游戏结束。

创建项目

制作游戏的第一步先创建项目,创建方法和上一节一样,使用 cargo 来创建一个名为 guessing_game 的项目。

cargo new guessing_game && cd guessing_game

项目创建完成,可以运行一下,如果程序打印出 Hello, World! 则证明程序创建完成,运行命令如下:

cargo run

读取猜测的数字

正式写游戏的第一步,让游戏先读取我们猜测的数字。我们可以先把打印语句换成提示我们输入数字的提示语句。

use std::io;

fn main() {
println!("猜测数字游戏,请输入您猜测的数字。"); let mut guess = String::new(); io::stdin().read_line(&mut guess).expect("读取数字失败!"); println!("您猜测的数字是:{}", guess);
}

这段代码包含了大量的信息,我们一行一行地过一遍。

1.因为我们需要读取用户的输入,然后把它作为结果打印出来,所以需要把 标准库(被称作 std )中的 io 依赖引入当前作用域。

2.在主函数中写方法体,首先是打印提示语,不说了。

3.然后创建一个用于保存即将输入的字符串的 String 类型的变量 guess。

4.把控制台输入的数字读取到变量 guess 中,如果读取失败,则打印 “读取数字失败!” 的字符串。

5.把读取的数字再打印到控制台。

注:这段程序的细节暂时先不深究了,后续文章会一一解释清楚。



测试一下这段程序:

cargo run
Compiling guessing_game v0.1.0 (/Users/shanpengfei/work/rust-work-space/study/guessing_game)
Finished dev [unoptimized + debuginfo] target(s) in 1.01s
Running `target/debug/guessing_game`
猜测数字游戏,请输入您猜测的数字。
2
您猜测的数字是:2

生成随机数

我们的游戏需要创建一个随机数,供我们去猜测,这个数字要求每次启动游戏时都是不相同的,这样游戏才更加有意思。接下来我们在游戏中生成一个1到100的随机数。但是 rust 没有在它的标准库中提供生成随机数的方法,不过没关系,它提供了生成随机数的名为 rand 的 crate。我们来引入一下生成随机数的 crate,修改 Cargo.toml 文件:

[dependencies]

rand = "^0.3.14"

只需要在 [dependencies] 下面添加需要的 crate 即可。这次添加的 crate 名字是 rand,版本号 0.3.14, 而 ^ 的意思是兼容 0.3.14 版本的任何版本都可以。然后我们编译一下程序,就会自动下载引入的依赖:

cargo build
Updating crates.io index
Compiling libc v0.2.65
Compiling rand v0.4.6
Compiling rand v0.3.23
Compiling guessing_game v0.1.0 (/Users/shanpengfei/work/rust-work-space/study/guessing_game)
Finished dev [unoptimized + debuginfo] target(s) in 1m 13s

引入了生成随机数和 crate 后,我们来生成一下需要的 crate,代码如下:

use std::io;
use rand::Rng; fn main() {
let secret_number = rand::thread_rng().gen_range(1, 101); println!("生成的随机数字是:{}", secret_number); println!("猜测数字游戏,请输入您猜测的数字。"); let mut guess = String::new(); io::stdin().read_line(&mut guess).expect("读取数字失败!"); println!("您猜测的数字是:{}", guess);
}

可以看到我们在前面代码的基础上添加了三行代码:

1.第一行是引入生成随机数的依赖。

2.第二行是生成一个随机数,随机数的范围是 [1, 101),区间是左闭右开,说人话就是1到100。

3.第三行是打印生成的随机数。

然后我们测试一下添加的随机数是否生效:

cargo run
Compiling guessing_game v0.1.0 (/Users/shanpengfei/work/rust-work-space/study/guessing_game)
Finished dev [unoptimized + debuginfo] target(s) in 0.45s
Running `target/debug/guessing_game`
生成的随机数字是:79
猜测数字游戏,请输入您猜测的数字。
6
您猜测的数字是:6

比较随机数和猜测数

现在我们可以输入自己猜测的数字,也可以生成随机数字了,那么接下来就是比较二者的大小了。但是在比较之前还有个问题,控制台输入的数字是 string 类型的,而随机生成的数字是无符号32位整型(u32),二者不类型不一致,不能作比较,因此,在比较之前,我们应该先把控制台输入的 string 类型的数字转成u32类型的,代码如下:

use std::io;
use std::cmp::Ordering;
use rand::Rng; fn main() {
let secret_number = rand::thread_rng().gen_range(1, 101); println!("生成的随机数字是:{}", secret_number); println!("猜测数字游戏,请输入您猜测的数字。"); let mut guess = String::new(); io::stdin().read_line(&mut guess).expect("读取数字失败!"); let guess: u32 = guess.trim().parse().expect("请输入一个数字!"); println!("您猜测的数字是:{}", guess); match guess.cmp(&secret_number) {
Ordering::Less => println!("您猜测的数字太小了!"),
Ordering::Greater => println!("您猜测的数字太大了!"),
Ordering::Equal => println!("恭喜您,猜对了!"),
}
}

可见,我们在三个位置添加了代码:

1.从标准库中添加了比较的依赖。

2.把输入的数字类型成u32类型,如果输入的不是数字,则转换失败,打印出错误信息。

3.最后一部分就是比较一下二者的大小,并打印出比较的结果。

好了,我们先测试一下吧,这里我们只测正确的输入:

cargo run                                     101 ↵
Compiling guessing_game v0.1.0 (/Users/shanpengfei/work/rust-work-space/study/guessing_game)
Finished dev [unoptimized + debuginfo] target(s) in 0.42s
Running `target/debug/guessing_game`
生成的随机数字是:53
猜测数字游戏,请输入您猜测的数字。
4
您猜测的数字是:4
您猜测的数字太小了!

添加循环

我们发现,我们只输入了一次,游戏就结束了,这显然不符合我们的预期。我们的预期是,我们可以一直猜一直猜,直到猜中才让游戏结束,那应该怎么修改一下呢?添加一个循环,代码如下:

use std::io;
use std::cmp::Ordering;
use rand::Rng; fn main() {
let secret_number = rand::thread_rng().gen_range(1, 101); println!("生成的随机数字是:{}", secret_number); loop { println!("猜测数字游戏,请输入您猜测的数字。"); let mut guess = String::new(); io::stdin().read_line(&mut guess).expect("读取数字失败!"); let guess: u32 = guess.trim().parse().expect("请输入一个数字!"); println!("您猜测的数字是:{}", guess); match guess.cmp(&secret_number) {
Ordering::Less => println!("您猜测的数字太小了!"),
Ordering::Greater => println!("您猜测的数字太大了!"),
Ordering::Equal => println!("恭喜您,猜对了!"),
}
}
}

这里修改得比较简单,只需要添加一个名叫 loop 的关键字,然后把需要循环的内容放在 {} 中即可,然后我们测试一下:

cargo run
Compiling guessing_game v0.1.0 (/Users/shanpengfei/work/rust-work-space/study/guessing_game)
Finished dev [unoptimized + debuginfo] target(s) in 0.38s
Running `target/debug/guessing_game`
生成的随机数字是:71
猜测数字游戏,请输入您猜测的数字。
50
您猜测的数字是:50
您猜测的数字太小了!
猜测数字游戏,请输入您猜测的数字。
71
您猜测的数字是:71
恭喜您,猜对了!
猜测数字游戏,请输入您猜测的数字。
45
您猜测的数字是:45
您猜测的数字太小了!
猜测数字游戏,请输入
t
thread 'main' panicked at '请输入一个数字!: ParseIntError { kind: InvalidDigit }', src/libcore/result.rs:1165:5
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace.

我们的游戏可以多次输入了,但是有没有发现一些问题呢?

1.游戏直接告诉我们生成的数字了,那就不用猜了,直接输入就好了。

2.当我们猜对后,游戏没有结束。

3.当我们输入的内容不是数字的时候,才会结束游戏,而且不仅打印了我们预期的错误信息,还打印了其它信息。

接下来,我们把这些问题依次修改,代码如下:

use std::io;
use std::cmp::Ordering;
use rand::Rng; fn main() {
let secret_number = rand::thread_rng().gen_range(1, 101); // println!("生成的随机数字是:{}", secret_number); loop { println!("猜测数字游戏,请输入您猜测的数字。"); let mut guess = String::new(); io::stdin().read_line(&mut guess).expect("读取数字失败!"); let guess: u32 = match guess.trim().parse() {
Ok(num) => num,
Err(_) => continue,
}; println!("您猜测的数字是:{}", guess); match guess.cmp(&secret_number) {
Ordering::Less => println!("您猜测的数字太小了!"),
Ordering::Greater => println!("您猜测的数字太大了!"),
Ordering::Equal => {
println!("恭喜您,猜对了!");
break;
}
}
}
}

这三处错误的修改方式依次是:

1.把打印随机数的代码注释掉。

2.在做类型转换时,使用 match 关键字作判断,如果转化成功,则返回转化后的结果,如果转化失败,不管因为什么原因失败,都直接跳出本次循环。

3.在做二个数字大小判断时,如果判断相等,则结束循环。

我们来测试一下修改的结果:

cargo run
Compiling guessing_game v0.1.0 (/Users/shanpengfei/work/rust-work-space/study/guessing_game)
Finished dev [unoptimized + debuginfo] target(s) in 0.38s
Running `target/debug/guessing_game`
猜测数字游戏,请输入您猜测的数字。
50
您猜测的数字是:50
您猜测的数字太小了!
猜测数字游戏,请输入您猜测的数字。
r
猜测数字游戏,请输入您猜测的数字。
75
您猜测的数字是:75
您猜测的数字太小了!
猜测数字游戏,请输入您猜测的数字。
87
您猜测的数字是:87
您猜测的数字太大了!
猜测数字游戏,请输入您猜测的数字。
81
您猜测的数字是:81
恭喜您,猜对了!

可以看到我们的游戏制作完成了~~

欢迎阅读单鹏飞的学习笔记

Rust 入门 (二)的更多相关文章

  1. 【原创】NIO框架入门(二):服务端基于MINA2的UDP双向通信Demo演示

    前言 NIO框架的流行,使得开发大并发.高性能的互联网服务端成为可能.这其中最流行的无非就是MINA和Netty了,MINA目前的主要版本是MINA2.而Netty的主要版本是Netty3和Netty ...

  2. Rust入门篇 (1)

    Rust入门篇 声明: 本文是在参考 The Rust Programming Language 和 Rust官方教程 中文版 写的. 个人学习用 再PS. 目录这东东果然是必须的... 找个时间生成 ...

  3. Swift语法基础入门二(数组, 字典, 字符串)

    Swift语法基础入门二(数组, 字典, 字符串) 数组(有序数据的集) *格式 : [] / Int / Array() let 不可变数组 var 可变数组 注意: 不需要改变集合的时候创建不可变 ...

  4. Thinkphp入门 二 —空操作、空模块、模块分组、前置操作、后置操作、跨模块调用(46)

    原文:Thinkphp入门 二 -空操作.空模块.模块分组.前置操作.后置操作.跨模块调用(46) [空操作处理] 看下列图: 实际情况:我们的User控制器没有hello()这个方法 一个对象去访问 ...

  5. DevExpress XtraReports 入门二 创建 data-aware(数据感知) 报表

    原文:DevExpress XtraReports 入门二 创建 data-aware(数据感知) 报表 本文只是为了帮助初次接触或是需要DevExpress XtraReports报表的人群使用的, ...

  6. css入门二-常用样式

    css入门二-常用样式总结 基本标签样式 背景色background-color 高度height; 宽度width; 边框对齐以及详细设定举例 width/*宽度*/: 80%; height/*高 ...

  7. 微服务(入门二):netcore通过consul注册服务

    基础准备 1.创建asp.net core Web 应用程序选择Api 2.appsettings.json 配置consul服务器地址,以及本机ip和端口号信息 { "Logging&qu ...

  8. IM开发者的零基础通信技术入门(二):通信交换技术的百年发展史(下)

    1.系列文章引言 1.1 适合谁来阅读? 本系列文章尽量使用最浅显易懂的文字.图片来组织内容,力求通信技术零基础的人群也能看懂.但个人建议,至少稍微了解过网络通信方面的知识后再看,会更有收获.如果您大 ...

  9. 脑残式网络编程入门(二):我们在读写Socket时,究竟在读写什么?

    1.引言 本文接上篇<脑残式网络编程入门(一):跟着动画来学TCP三次握手和四次挥手>,继续脑残式的网络编程知识学习 ^_^. 套接字socket是大多数程序员都非常熟悉的概念,它是计算机 ...

随机推荐

  1. config.xml

    ASP.NET应用程序的配置信息都存放于Web.config配置文件中,Web.config配置文件是基于XML格式的文件类型,由于XML文件的可伸缩性,使得ASP.NET应用配置变得灵活.高效.容易 ...

  2. 拨云见日,彻底弄清楚Java日志框架 log4j, logback, slf4j的区别与联系

    log4j 以及 logback, slf4j 官网 日志框架的困惑 作为一个正常的项目,是必须有日志框架的存在的,没有日志,很难追踪一些奇奇怪怪的系统问题. 但是,我们经常在项目的依赖中,见到奇奇怪 ...

  3. day26作业

    1.整理TCP三次握手.四次挥手图 2.基于TCP开发一款远程CMD程序 客户端连接服务器后,可以向服务器发送命令 服务器收到命令后执行,无论执行是否成功,无论执行几遍,都将执行结果返回给客户端 注意 ...

  4. Spark集群安装与配置

    一.Scala安装 1.https://www.scala-lang.org/download/2.11.12.html下载并复制到/home/jun下解压 [jun@master ~]$ cd sc ...

  5. Docker 实战—使用 Dockerfile 构建镜像

    Dockerfile 指令详解请访问:https://www.cnblogs.com/cloudfloating/p/11737447.html 使用 Alpine Linux 作为基础镜像 Alpi ...

  6. C语言中为什么float型数据的范围是3.4E-38~3.4E+38

    因为float所占的位数决定了他的大小位数就是计算机的存储所需要的bit多少32位浮点,64位双精度浮点范围不同//////////////////////以前学计算系统基础的时候有这么个说法计算机存 ...

  7. 如何学好javascript

    今天逛论坛时看到有朋友问,是否有专门教Javascript的学校,这里想想把自己的一点建议和自己3年来的前端Javascript开发的经验跟大家分享下,也给出几本个人认为不错的书来做为大家学习的参考资 ...

  8. Token refresh的实现

    实现原理: 在access_token里加入refresh_token标识,给access_token设置短时间的期限(例如一天),给refresh_token设置长时间的期限(例如七天).当活动用户 ...

  9. Rest_Framework之频率组件部分

    一.RestFramework之频率组件源码部分 频率组件的源码部分和权限组件流程一模一样的,这里就不多说了,直接上源码的主要逻辑部分: def check_throttles(self, reque ...

  10. Ubuntu16.04下升级Python到3.6

    转: 这里 有一篇帖子是说从源代码开始安装,这种方式原来尝试过,需要删除系统默认的软链命令,感觉比较粗暴,现在在想有没有更好的方式呢? 找到一个帖子:http://ubuntuhandbook.org ...