第二章 RUST通用编程概念
文章目录
前言
官方文档有一个猜数游戏,喜欢的可以去试试,本篇直接进入主题。
一、变量与可变性
rust中的变量默认是不可变的。
fn main() {
let x = 5;
println!("变量X的值是:{}",x);
x = 6;
println!("新X变量的值是:{}",x);
}
输出报错:
意思是不可对不可变变量进行二次赋值。可以在声明的变量前加mut关键字使其可变。
fn main() {
let mut x = 5;
println!("变量X的值是:{}",x);
x = 6;
println!("新变量x的值是:{}",x);
}
变量与常量之间的不同
1、绑定到常量上的值无法被其他代码修改。
2、不能用mut关键字修饰一个常量。
3、使用const关键字来声明常量而不是let。
4、常量可以被声明在任何作用域中,包括全局作用域。
5、只能将常量绑定到一个常量表达式上,而无法将一个函数的返回值,或其他需要在运行时计算的值绑定到常量上。
const MAX_POINTS:u32 = 100_000;
6、约定俗成的使用以下划线分割的全大写字母来命名一个常量,并在数值中可以插入下划线提高可读性。
隐藏shadow
先看代码
fn main() {
let x = 5;
let x = x + 1;
let x = x + 2;
println!("x的值是{}",x);
}
在rust世界中,一个新声明的变量可以覆盖掉旧的同名变量,这种现象,叫做第一个变量被第二个变量隐藏了。代表之后使用这个名称时,都是指向第二个变量。可重复隐藏。
对比mut理解,x变量不可变但可通过隐藏创建出新的变量,并且可以同时改变原来的类型。
(输入的空格存储为一个独立的值)
fn main() {
let spaces = " ";
let spaces = spaces.len();
println!("变量的值是{}",spaces);
}
不需要做出诸如spaces_str和spaces_num之类的区分。用mut会报错。
fn main() {
let mut spaces = " ";
spaces = spaces.len();
println!("变量的值是{}",spaces);
}
拒绝修改类型。
二、数据类型
rust是静态类型语言,它在编译过程中需要知道所有变量的具体类型。编译器会默认给变量添加类型,当编译器识别不出来时就需要手动添加了。
标量类型
标量类型是单个值类型的统称,共有四个基础的标量类型。
整数、浮点数、布尔值及字符。
整数:
1、指的是没有小数部分的数字。
2、符号指的是正负。
3、不声明默认为i32。
4、isize与usize由计算机架构决定,32或64等。
5、i8能存储-128~127之间的所有整数,u8能存储0-255之间的所有整数。就是2的8次方减1,256-1=255。
长度 | 有符号 | 无符号 |
---|---|---|
8-bit | i8 | u8 |
16-bit | i16 | u16 |
32-bit | i32 | u32 |
64-bit | i64 | u64 |
arch | isize | usize |
6、可以使用下表列出的所有方式在代码中书写整数字面量,除了Byte,其余所有字面量都可以使用类型后缀,比如57u8,代表一个使用了u8类型的整数57。
整数字面量 | 翻译 | 示例 |
---|---|---|
Decimal | 十进制 | 98_222 |
Hex | 十六进制 | 0xff |
Octal | 八进制 | 0o77 |
Binary | 二进制 | 0b1111_0000 |
Byte(u8 only) | 字节 | b’A’ |
整数溢出:
当一个u8类型的变量,它可以存储从0到255的数字,当尝试将该变量修改为超过范围的值(256),就会发生整数溢出。
fn main() {
let x:u8 = 256;
println!("变量的值是{}",x);
}
新版本的release一样触发了报错,可以自行尝试,书上说的是会环绕为类型的最小值,256变成0,257变成1。
希望显式地进行环绕行为,可以使用标准库中的类型Wrapping。
浮点数类型
1、指小数。
2、默认为f64。
3、Rust的浮点数使用了IEEE-754标准来进行表述,f32对应单精度浮点数,f64对应双精度浮点数。
fn main(){
let x = 2.0;//f64
let y:f32 = 3.0;//f32
}
数值运算
加减乘除和取余。
fn main() {
let sum = 5 + 10;
let difference = 95.5-4.3;
let product = 4 * 30;
let quotient = 56.7 / 32.2;
let remainder = 43 % 5;
println!("加{},减{},乘{},除{},取余{}",sum,difference,product,quotient,remainder);
}
布尔类型
1、true与false。
2、占据1个字节大小。
3、使用bool表示一个布尔类型。
let f:bool = false;
4、主要作用是在if表达式内作为条件使用。
字符类型
1、char类型被用于描述最基础的单个字符。
2、char类型使用单引号指定,不同于字符串使用双引号指定。
3、char类型占4字节,是一个Unicode标量值,可以表示拼音字母、中文、日韩文、空白字符甚至emoji表情。(U+0000~U+D7FF以及从U+E000到U+10FFFF的所有值)
复合类型
1、复合类型可以将多个不同类型组合为一个类型。
2、rust内置的基础复合类型:元组tuple和数组array。
元组类型
1、可以将不同类型的多个值组合进一个复合类型。
2、元组具有固定长度,无法在声明后增减其中的元素数量。
let tup: (i32,f64,u8) = (500,6.4,1);
3、元组也被视作一个单独的复合元素。
4、可以使用模式匹配的方法来解构元组,从元组中获得单个的值。
fn main() {
let tup = (500,6.4,1);
let (x,y,z) = tup;
println!("x={},y={},z={}",x,y,z);
}
5、除了解构,还可以通过索引并使用点号(.)来访问元组中的值。
fn main() {
let a: (i32,f64,u8) = (500,6.4,1);
//创建元组a
let x = a.0;
//通过索引访问元组的各个元素,并绑定它们的值到新的变量上。
let y = a.1;
let z = a.2;
println!("x={},y={},z={}",x,y,z);
}
数组类型
1、与元组不同,数组中的元素的类型必须是相同的
2、具有固定长度,不可变。
let a:[i32;5] = [1,2,3,4,5];
tips:动态数组vector允许调整数组长度。详情第七章,现在仅做了解。
语法糖:
let a = [3;5];
//等价于
let a = [3,3,3,3,3];
访问数组的元素
let a = [1,2,3,4];
let first = a[0];
非法的数组元素访问
fn main() {
let a = [ 1,2,3,4,5];
let index = 10;
let element = a[index];
println!("element的值为{}",element);
}
这段代码可以通过编译,但是实际运行会报错并崩溃退出。当通过索引来访问一个元素时,rust会进行检查,当超出了当前长度,则会发生panic(第8章)。
三、函数
1、rust采用蛇形命名法,只是用小写字母命名函数,可使用下划线分割单词。
2、函数定义以fn关键字并紧随函数名与一对圆括号,还有一对花括号用于标识函数体开始和结尾的地方。
fn abc(){}
3、rust不关心在何处定义函数,只要定义对于使用区域可见。
函数参数
1、可以在函数声明中定义参数,作为特殊变量视为函数签名的一部分。
2、必须显式声明每个参数的类型。
fn main() {
abc(5,6);
}
fn abc(x:i32,y:i32){
println!("x={}",x);
println!("y={}",y);
}
函数体中的语句和表达式
1、rust是一门基于表达式的语言,它将语句与表达式区别为两个不同的概念。
2、语句指的是执行操作但不返回值得指令,表达式是指进行计算并产生一个值作为结果的指令。看下列代码增强理解。
fn main(){
let y = 6;
//使用let关键字创建变量并绑定值,let y = 6;就是一条语句。
}
//
fn main(){
let x = (let y = 6);
}
//该程序会报错,语句不会返回值,x没有可以绑定的东西,所以不能将一条let语句赋值给另一个变量。
fn main() {
let x = 5;
let y = {
let x = 3;
x + 1
};//该分号位置需要注意,x+1后未加分号。
println!("x={}",x);
println!("y={}",y);
}
3、表达式会计算出某个值作为结果。在rust中编写的大部分代码都会是表达式。
4、表达式本身也可以作为语句的一部分。let y = 6;中,字面量6就是表达式,它返回6作为自己的计算结果。
5、5+6是表达式,调用函数是表达式,调用宏是表达式,花括号也是表达式。
6、可由分号位置简单判断是否是语句或表达式。
函数的返回值
1、函数可以向调用它的代码返回值,可以不用对这个值命名,但需要在箭头符号后面声明它的类型。
2、在rust中,函数的返回值等同于函数最后一个表达式的值,可以使用return关键字并指定一个值来提前从函数中返回,大多数函数都隐式地返回了最后的表达式。
fn five() -> i32{//指定类型
5
}//该函数没有任何其他的函数调用、宏调用,let语句,但也是一个有效的函数。
fn main(){
let x = five();
println!("x={}",x);
}
//x=5
fn main(){
let x = plus_one(5);
println!("x={}",x);
}
fn plus_one(x:i32) -> i32{
x+1//这里不能加分号,加分号默认为语句,返回空元组,与i32产生矛盾
}
//x=6
四、注释
1、常用//,/**/也行。
tips:13章会介绍///和//!
五、控制流
在rust中用来控制程序执行流的结构主要就是if表达式与循环表达式。
if 表达式
1、if…else,如果…则,假如满足条件,则运行这段代码,否则运行另一个代码。
fn main(){
let number = 3;
if number < 5 {//条件
println!("true");//分支(arm)
}else{//如果没有提供else,且条件判断为假,则继续向下执行代码。
println!{"false"};
}
}
//true
2、代码中的条件表达式必须产生一个bool类型的值,否则会触发编译错误。rust不会自动尝试将非布尔类型的值转换为布尔类型。(不同于Ruby与JavaScript)
3、所有if分支可能返回的类型必须是同一类型,否则会报错。
使用else if 实现多重条件判断
if…else if…else if…else
多个条件。因为rust中有更强大的语法match,不做过多描述。
在let语句中使用if
fn main(){
let y = true;
let x = if y{
5
}else{
6
};
println!{"x={}",x};
}
//x=5
使用循环重复执行代码
使用loop重复执行代码
1、loop无限循环。
2、通过break终止循环。
从loop循环中返回值
fn main(){
let mut counter = 0;
let result = loop {
counter += 1;//相加并赋值
if counter == 10 {//相等性比较
break counter * 2;
}
};
println!("result={}",result);
}
//result=20
while 循环
1、条件循环,每次循环都判断一次条件。
fn main(){
let mut number = 3;
while number != 0{//不等于
println!("{}!",number);
number = number - 1;
}
println!("结束");
}
//3!2!1!结束
使用for来循环遍历集合
1、更安全简单快捷,
fn main(){
for number in (1..4).rev(){//rev反转
println!("{}!",number);
}
println!("结束");
}
//结果与while相同
作业
1、摄氏温度与华氏温度的互相转换。
2、生成一个n阶的斐波那锲数列。
3、打印圣诞颂歌The Twelve Days of Christmas的歌词,并利用循环处理其中重复的内容。