Rust学习日记(二)变量的使用--结合--温度换算/斐波那契数列--实例

前言:
这是一个系列的学习笔记,会将笔者学习Rust语言的心得记录。
当然,这并非是流水账似的记录,而是结合实际程序项目的记录,如果你也对Rust感兴趣,那么我们可以一起交流探讨,使用Rust来构建程序。

注:本文中使用Rust都是在windows环境下,如果是macOS或者linux,其指令或有不同,请注意。

系列第一篇:Rust学习日记(一)Cargo的使用

概述:

这是Rust学习笔记的第二篇,主要说一下Rust中的变量,本文将结合两个实例来说明,这两个实例分别是:
1、在华氏度和摄氏度之间转换温度
2、生成指定个数(n)的斐波那契数列
如果你看过rust的官方手册,那你会熟悉,这两个例子事实上就是官方留的“课后习题”

在进入实例之前,还是要先来简单看看变量的使用。

变量的申明

1、关键词let
在rust中,申明变量的关键词就是let。如果你使用过其他语言,那么你可能对rust的变量申明有疑问,为什么用let?
我觉得不用纠结,这就是一个定义,使用什么单词来作为关键词并不重要,重要的是,这个关键词的作用。

fn main() {let x=5;println!("x is {x}");
}

上面的示例程序段,是在主函数中创建了变量x,并打印,在终端输入cargo run,输出如下:

PS D:\008 rustpro\var> cargo runFinished dev [unoptimized + debuginfo] target(s) in 0.02sRunning `target\debug\var.exe`
x is 5

2、变量的可变与不可变
变量,顾名思义,就是可变化的量,不可变的是变量吗?不可变的应该是常量吧?
在rust中,默认情况下,变量是不可变的。
这是rust的设计,它的目的,当然是为了安全。“安全”是贯穿rust整个体系的,后面会有更多更复杂更高级的功能,都会和它有关。

fn main() {let x=5;println!("x is {x}");x=x+1;println!("x is {x}");
}

对前面的函数稍作修改,给变量x再次赋值,再看输出:

PS D:\008 rustpro\var> cargo runCompiling var v0.1.0 (D:\008 rustpro\var)
error[E0384]: cannot assign twice to immutable variable `x`--> src\main.rs:4:5|
2 |     let x=5;|         -|         ||         first assignment to `x`|         help: consider making this binding mutable: `mut x`
3 |     println!("x is {x}");
4 |     x=x+1;|     ^^^^^ cannot assign twice to immutable variableFor more information about this error, try `rustc --explain E0384`.
error: could not compile `var` (bin "var") due to previous error

错误提示,cannot assign twice to immutable variable,不能给“immutable variable”分配两次,什么叫immutable variable?就是不可变变量。
所以,现在你就知道,rust是比较奇葩的了。
但是,这就是rust的设计,如果你定义变量的时候,只是简单的let x=5;那么这个x是不可变的。那如果要可变呢:

let mut x=5

再加一个关键词mut,mut其实就是mutable的简写,可变的。

fn main() {let mut x=5;println!("x is {x}");x=x+1;println!("x is {x}");
}

cargo run:

PS D:\008 rustpro\var> cargo runCompiling var v0.1.0 (D:\008 rustpro\var)Finished dev [unoptimized + debuginfo] target(s) in 0.43sRunning `target\debug\var.exe`
x is 5
x is 6

可以发现,“变量x”的值可以多次分配,也就是真正的可变了。
看到这里你发现什么?
你会发现,在rust里,你对一个对象,比如变量的定义,必须要十分清楚,可变就可变,不可变就不可变,
这样一来,rust编译器就能非常清楚地知道,你定义这个变量的目的,它会编译过程对你的意图全程跟踪,
不可变变量如果不小心让它变化,编译器就会报错,提示你,对你来说,这就是安全。
你不用等程序都运行了,然后偶尔崩溃了一次,然后去找bug,找来找去无论怎么编译就是没问题,但一运行就报错,这是令人崩溃的。

你已经了解到rust中变量的奇葩性了,我们接着再看几个,然后再进入实例。

3、常量
rust中常量用const来申明,这个const应该是比较熟悉的单词了,很多编程语言申明常量都是用它吧。
const也就是Constants的简写。
看一下申明的格式吧:

const C_1=5

如果你真的这么写,cargo check一下,会报错:

PS D:\008 rustpro\var> cargo checkChecking var v0.1.0 (D:\008 rustpro\var)
error: missing type for `const` item--> src\main.rs:6:14|
6 |     const C_1=5;|              ^ help: provide a type for the constant: `: i32`error: could not compile `var` (bin "var") due to previous error

rust编译器提示你,需要给常量提供一个数据类型,并且给了你一个选项:i32
rust中的数据类型----整数型:
在这里插入图片描述
关于数据类型,其实我认为不用多说,照着手册看看了解一下即可,因为一般来说,对编程有所了解的,对数据类型一般都不陌生,可以说,几乎所有语言中的数据类型,都大同小异。
常见的比如说整数、浮点数、布尔量、字符串等,还有一些复杂的,比如结构体(复合体)、数组、列表、元组等。

我们接着说常量,上面说这样写const C_1=5;会报错,原因是未给常量提供数据类型,所以我们应该这么来申明常量

const C_1:i32=5

rust就是这样,它要求你明确,哪怕是常量,它不会为你隐式转换,它要求你定义常量时,明确常量的数据类型。
到此,你会发现,rust似乎很“麻烦”,这也要明确,那也要明确,为什么不能像其他语言一样,直接a=5;或者const a=5;
我只能说,rust它就是这样一门语言,如果你对此实在不能接受,那么放弃是好的,如果无所谓,那么就按照rust的规则,暂时先走下去。

4、变量的覆盖
我们在本文前面说过,rust中的变量有可变和不可变的区别,直接申明变量,默认不可变,如果要可变,需加关键词mut。
但现在我们来看另一种使用:

fn main() {let x=5;println!("x is {x}");let x:char='a';println!("x is {x}");
}

我们在第一行定义x变量,且赋值为5,但没有用mut使其可变,所以x是不可变的,但是我们在第三行再次使用x变量,而且给它赋值为字符‘a’。
我们先cargo run一下:

PS D:\008 rustpro\var> cargo runCompiling var v0.1.0 (D:\008 rustpro\var)Finished dev [unoptimized + debuginfo] target(s) in 0.63sRunning `target\debug\var.exe`
x is 5
x is a

可以看到,rust正常编译,且结果x的值被改变了。你应该看到区别,就是第三行再次使用x时,重新定义了一次。
rust中,用let来定义变量,是可以重名的,并且后一个可以覆盖前面的,所谓“shadow”,即旧的变量活在新的变量的阴影里
你可能会觉得,这样是不是会造成困扰,如果不小心使用了一样的变量名,覆盖之前的变量数据,岂不是会影响程序。但这个问题容易解决,只需要你在命名变量时有自己的规律即可,但是rust中这样的机制会带来好处。
我们看到,同样的变量x,在第一次和第二次的定义中,不仅值可以不同,连数据类型也可以不同。
什么场景会这样使用,比如你要输入一个值,是字符类型,但程序接收字符后,要以整数型来运算,但是这两个其实一个东西,如果是其他语言,你可能会这样定义变量,x_str和x_int,但是rust中你可以只定义一个x,

let x:char='a';
let x:i32=5;

好了,关于变量就先说到这,基本上差不多了,当然这并非是说把变量的所有方面都涉及到了,只是说基本的概念都已经了解了。
下面将开始实例:

一、温度转换

1、新建一个temp项目

PS D:\008 rustpro> cargo new tempCreated binary (application) `temp` package
PS D:\008 rustpro> cd temp
PS D:\008 rustpro\temp> cargo buildCompiling temp v0.1.0 (D:\008 rustpro\temp)Finished dev [unoptimized + debuginfo] target(s) in 0.49s
PS D:\008 rustpro\temp> 

在这里插入图片描述
新建项目后,rust会自动生成一个main.rs主函数。
这个主函数里默认的是打印“hello world”,我们要修改它,以达到温度转换的功能。
华氏度和摄氏度的转换关系如下:

1 摄氏度=33.8 华氏度

所以,如果我们输入一个摄氏度值为x,那么转换为华氏度应该是:f(x)=33.8*x
数学关系很清楚,是很简单的一元一次函数。
先看一下main.rs的结构:

fn main() {println!("Hello, world!");
}

fnlet一样,是关键词,定义函数,所以fn后面的main就是函数,只不过它是主函数,所以地位特殊,但是,main这个名字,想必大家也不陌生,无论是C还是python,都有主函数,地位也差不多,谁让它是main呢!
但我们先不修改main,我们先新建一个函数,就叫temp_convert:

fn temp_convert(){}

添加逻辑代码:

fn temp_convert(t:f32)->f32{let t2=t*33.8;return t2;
}

可以看到,我们在新建的函数基础上,添加了一个形式参数t,把它的数据类型定义为f32,即浮点数。
定义了一个函数返回值类型:->f32,即函数返回值也是f32类型。
然后在函数体内,定义了变量t2,其赋值为t*33.8,
最后将计算后的t2的值返回。
这个函数中形式参数t即输入的摄氏度,返回的t2即转换的华氏度。
接下来,就是在主程序里调用了:
修改一下main函数:

fn main() {let t1=temp_convert(3.0);println!("t_f is {t1}");
}

因为函数temp_convert有返回值,所以我们定义一个变量t1来储存它。
到此,简单的温度转换就实现了,只要给函数的参数赋予不同的值(摄氏度),函数就可以返回华氏度:

PS D:\008 rustpro\temp> cargo runCompiling temp v0.1.0 (D:\008 rustpro\temp)Finished dev [unoptimized + debuginfo] target(s) in 0.42sRunning `target\debug\temp.exe`
t_f is 101.399994

但这样肯定不算是一个完整的程序,我们设想应该是这样,程序启动,会输出一个文字提示,让我们输入一个摄氏度,我们输入后,程序输出一个华氏度。然后能循环,这样才算是完整的转换程序。
我们分两部分来说,一是如何接受用户输入,二是如何循环。

1 用户输入

用户输入,需要用到IO接口,在rust中即std::io
std::io,这是一个程序包crate,类似于其他程序的模块。在使用它之前,需要先导入它。

use std::io;

然后使用它:

use std::io;
fn main() {let mut t_in=String::new();println!("请输入摄氏度值:");io::stdin().read_line(&mut t_in).expect("error");let t_in:f32=t_in.trim().parse().expect("error");let t1=temp_convert(t_in);println!("华氏度是 {t1}");
}

上面的主函数main变化较大,我们一一来说一下。我们使用use关键词导入了std::io,这是用来接受用户输入的模块。
然后在主函数体内添加程序:

let mut t_in=String::new();

这行是定义了一个可变变量t_in且类型为string

  io::stdin().read_line(&mut t_in).expect("error");

这行就是io接口的调用,用于读取用户的输入,read_line即是std::io模块的函数。&mut t_in是将我们定义的变量t_in来接受read_line的输入值。
其实上面这行直接写如下:

io::stdin().read_line(&mut t_in).expect("error");

之所以写成多行样式,是为了便于阅读。
这里的expect是对潜在错误的处理,关于错误的处理不在本文的范围,后续会有专门叙述。此处只要知道这样用就可以了。
我们现在已经从用户处接受到输入值,即t_in,接下来要对输入值进行处理:

let t_in:f32=t_in.trim().parse().expect("error");

此处我们使用前面介绍的变量覆盖,先定义了t_in为用户输入,且数据类型为string类型,接着此行用同名变量t_in定义为f32类型,覆盖之前的字符串类型。
其中的trim是消除一个字符串的开头和结尾的多余空格,parse是将字符串转为另一种数据类型,在此处就是转为f32类型。expect我们已经说过,是对返回值的判断时可能发生的错误处理。

`let t1=temp_convert(t_in);

在顺利获得用户输入的值且转为浮点类型后,我们调用写好的temp_convert转换函数,其形式参数t在调用时传入实际参数t_in。将temp_convert函数的返回值,传给变量t1。
完整程序:

use std::io;
fn main() {let mut t_in=String::new();println!("请输入摄氏度值:");io::stdin().read_line(&mut t_in).expect("error");let t_in:f32=t_in.trim().parse().expect("error");let t1=temp_convert(t_in);println!("华氏度是 {t1}");
}
fn temp_convert(t:f32)->f32{let t2=t*33.8;return t2;
}

在终端运行cargo run:

PS D:\008 rustpro\temp> cargo runCompiling temp v0.1.0 (D:\008 rustpro\temp)Finished dev [unoptimized + debuginfo] target(s) in 0.74sRunning `target\debug\temp.exe`
请输入摄氏度值:
2
华氏度是 67.6

好了,第一部分完成了,现在我们能成功根据输入的摄氏度转为华氏度且输出,但缺点是每次运行只能进行一次,所以需要循环。

循环

想让程序循环起来,也很简单,使用loop指令,它会循环执行主程序的逻辑,直到触发停止。本文中未添加明确的停止程序,因为这不在我们的学习范围,后续会有说明,此文不赘述。

fn main() {loop{let mut t_in=String::new();println!("请输入摄氏度值:");io::stdin().read_line(&mut t_in).expect("error");let t_in:f32=t_in.trim().parse().expect("error");let t1=temp_convert(t_in);println!("华氏度是 {t1}");}}

我们可以看到,只需要将main中的原程序添加进loop的{}中即可。
让我们运行一下:

PS D:\008 rustpro\temp> cargo runCompiling temp v0.1.0 (D:\008 rustpro\temp)Finished dev [unoptimized + debuginfo] target(s) in 0.46sRunning `target\debug\temp.exe`
请输入摄氏度值:
2
华氏度是 67.6
请输入摄氏度值:
3
华氏度是 101.399994
请输入摄氏度值:
4
华氏度是 135.2
请输入摄氏度值:
1
华氏度是 33.8
请输入摄氏度值:
2
华氏度是 67.6
请输入摄氏度值:
r
thread 'main' panicked at src\main.rs:10:42:
error: ParseFloatError { kind: Invalid }
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
error: process didn't exit successfully: `target\debug\temp.exe` (exit code: 101)

可以看到,程序在不停地执行,如果用户一直输入正确的值得话,但你会发现在终端的记录中,最后输入r时,rust报错且退出了,这是因为程序中没有对非法输入的应对措施,当输入非法值时,只能报错退出。
但我不准备在本文中完善它,我相信随着我们学习rust的深入,这个问题会迎刃而解。
如果你看到这里,我相信你对rust中变量、函数的使用,有了初步的印象了。
下面我们说一下第二个实例,即实现斐波那契数列。

斐波那契数列

斐波那契数列应该是比较经典的练手题目了,你在学习其他语言如C、python、java中都可以通过编写斐波那契数列来加深对语言的理解。
rust也不例外,我在文章一开头就说过,此文所列举的两个实例,都是rust官方手册中给读者留的课后习题。
其实官方在手册的第二章给了一个很好的实例即猜谜游戏,说的非常详细,但我们作为学习者,在学习了官方手册后,自己要学会应用,所以我选择用课后习题来作为示例。

斐波那契数列是这样的数列:

1,1,2,3,5,8,13,21,34,55,89…

这个数列的规律是,从第三项开始,每一项的值都是它前面两项的和。用公式表示为:

f(n)=f(n-1)+f(n-2);n>2

所以,如果要用函数来实现斐波那契数列,函数肯定是递归的。

rust实现,首先是创建新项目,这个不用多说了。
在这里插入图片描述
接着创建新的函数,命名为fibonacci

fn fibonacci(num:i32)->i32{if num<3{return 1;}else{return fibonacci(num-1)+fibonacci(num-2);}
}

这个fibonacci函数有个形式参数num,数据类型为i32,函数的返回值类型由’->'符号指出,也是i32
函数体内的逻辑,是对num进行判断,如果num小于3,也就是fibonacci数列的前2项,那么函数返回值固定为1,如果num大于等于3,那么返回值就等于前2项之和。
此处的判断,使用了if。。else语句。if else应该不用多说了吧,这应该是学习编程的人,都会了解的。
几乎你学习任何编程语言,都会遇到if语句。
所以,本文也不会赘述if。。else语句。
接下来看main函数:

use std::io;
fn main() {loop{println!("please enter fibonacci number:");let mut number=String::new();io::stdin().read_line(&mut number).expect("err");let number:i32=number.trim().parse().expect("err");println!("number is {number}");for y in 1..number{let x=fibonacci(y);println!("fibo({y}) is {x}");}}  }

可以看到,几乎和前面的实例一样,一点区别在于子函数fibonacci的调用处,这里用了for语句:
之所以使用for语句,因为斐波那契数列是一个数列,当我们给定一个斐波那契数列的项索引(n)时,我们实际上希望程序能够列出至n项的斐波那契数列所有的项。

 for y in 1..number{let x=fibonacci(y);println!("fibo({y}) is {x}");}

同理,for循环也是常见的,和if语句一样,我们会在后续的文章里统一说明,此文不赘述。
我们还是看一下实际运行效果吧:

PS D:\008 rustpro\fibonacci> cargo runCompiling fibonacci v0.1.0 (D:\008 rustpro\fibonacci)Finished dev [unoptimized + debuginfo] target(s) in 0.71sRunning `target\debug\fibonacci.exe`
please enter fibonacci number:
2
number is 2
fibo(1) is 1
please enter fibonacci number:
5
number is 5
fibo(1) is 1
fibo(2) is 1
fibo(3) is 2
fibo(4) is 3
please enter fibonacci number:
10
number is 10
fibo(1) is 1
fibo(2) is 1
fibo(3) is 2
fibo(4) is 3
fibo(5) is 5
fibo(6) is 8
fibo(7) is 13
fibo(8) is 21
fibo(9) is 34
please enter fibonacci number:
r
thread 'main' panicked at src\main.rs:10:46:
err: ParseIntError { kind: InvalidDigit }
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
error: process didn't exit successfully: `target\debug\fibonacci.exe` (exit code: 101)

总结一下,rust变量:

let x=5;//x不可变
let mut x=5;//x可变
let x=5;
let x='a';//x依旧不可变,但可以被重名者覆盖,且可不同数据类型
const x:i32=5;//常量需指定数据类型

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/160895.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

CMU/MIT/清华/Umass提出生成式机器人智能体RoboGen

文章目录 导读1. Introduction2. 论文地址3. 项目主页4. 开源地址5. RoboGen Pipeline6. Experimental Results作者介绍Reference 导读 CMU/MIT/清华/Umass提出的全球首个生成式机器人智能体RoboGen&#xff0c;可以无限生成数据&#xff0c;让机器人7*24小时永不停歇地训练。…

7.spark sql编程

概述 spark 版本为 3.2.4&#xff0c;注意 RDD 转 DataFrame 的代码出现的问题及解决方案 本文目标如下&#xff1a; RDD ,Datasets,DataFrames 之间的区别入门 SparkSession创建 DataFramesDataFrame 操作编程方式运行 sql 查询创建 DatasetsDataFrames 与 RDDs 互相转换 使用…

Deep Image Matting:深度学习Matting开山之作

论文&#xff1a;https://arxiv.org/pdf/1703.03872.pdf 代码&#xff1a;GitHub - foamliu/Deep-Image-Matting-PyTorch: Deep Image Matting implementation in PyTorch 1、动机 在此之前的Matting方法要么是用传统方法&#xff0c;要么是传统深度学习的组合&#xff0c;他们…

亚马逊云科技大语言模型下的六大创新应用功能

目录 前言 亚马逊云科技的AI创新应用 ​编辑 Amazon CodeWhisperer Amazon CodeWhisperer产品的优势 更快地完成更多工作 自信地进行编码 增强代码安全性 使用收藏夹工具 自定义 CodeWhisperer 以获得更好的建议 如何使用Amazon CodeWhisperer 步骤 1 步骤 2 具体…

2023年金融科技建模大赛(初赛)开箱点评-基于四川新网银行数据集

各位同学大家好&#xff0c;我是Toby老师。2023年金融科技建模大赛&#xff08;初赛&#xff09;从今年10月14日开始&#xff0c;11月11日结束。 比赛背景 发展数字经济是“十四五”时期的重大战略规划。2023年&#xff0c;中共中央、国务院印发了《数字中国建设整体布局规划》…

【漏洞复现】​金蝶云星空管理中心

目录 0x01 漏洞介绍 0x02 影响产品 0x03 语法特征 0x04 漏洞复现 0x01 漏洞介绍 金蝶云星空是一款基于领先的可组装低代码PaaS平台&#xff0c;全面服务客户研发、生产、营销、供应链、财务等领域转型的企业管理服务平台。它已支持IPD、精益、阿米巴等先进管理理念在3.1万余…

一站式解决方案:体验亚马逊轻量服务器/VPS的顶级服务与灵活性

文章目录 一、什么是轻量级服务器/VPS 二、服务器创建步骤 三、服务器连接客户端(私钥登录) 四、使用服务器搭建博客网站 五、个人浅解及总结 一、什么是轻量级服务器/VPS 亚马逊推出的轻量级服务器/VPS&#xff1a;是一种基于云计算技术的虚拟服务器解决方案。它允许用户…

JAVA毕业设计109—基于Java+Springboot+Vue的宿舍管理系统(源码+数据库)

基于JavaSpringbootVue的宿舍管理系统(源码数据库)109 一、系统介绍 本系统前后端分离 本系统分为学生、宿管、超级管理员三种角色 1、用户&#xff1a; 登录、我的宿舍、申请调宿、报修申请、水电费管理、卫生检查、个人信息修改。 2、宿管&#xff1a; 登录、用户管理…

MES与ERP系统集成的一些探讨

什么是MES软件&#xff1f; 制造执行系统 &#xff08;MES&#xff09; 是一种用于控制车间复杂制造操作和数据的软件。MES软件有助于提高生产过程的质量&#xff0c;使制造商能够轻松响应需求和客户偏好的变化。 MES软件有什么作用&#xff1f; 制造执行系统允许企业跟踪、…

【Unity细节】论复合函数在Start生命周期中的调用顺序和时间

&#x1f468;‍&#x1f4bb;个人主页&#xff1a;元宇宙-秩沅 hallo 欢迎 点赞&#x1f44d; 收藏⭐ 留言&#x1f4dd; 加关注✅! 本文由 秩沅 原创 &#x1f636;‍&#x1f32b;️收录于专栏&#xff1a;unity细节和bug &#x1f636;‍&#x1f32b;️优质专栏 ⭐【…

【云服务器】对比传统服务器,为什么说云服务器更具优势?

个人主页&#xff1a;【&#x1f60a;个人主页】 系列专栏&#xff1a;【❤️其他领域】 文章目录 前言云服务器云服务器的优势成本可扩展性可靠性和安全性 总结 前言 2006年搜索引擎大会上&#xff0c;“云服务器”的概念孕育而生&#xff0c;时至今日云服务器与传统服务器的…

高德Go生态建设与研发实践

序 高德在构建Go生态演化过程中&#xff0c;已经实现了QPS从0到峰值千万的飞跃&#xff0c;本篇文章主要介绍在此过程中积累的一些技术决策及性能优化和重构经验。阅读本文读者会有以下3点收获&#xff1a; 1.高德Go生态发展历程及现状分析 2.高德云原生Serverless落地情况&…