一、变量与常量
1.1 变量
1.1.1. 变量命名
变量名由字母、数字或下划线组成,应该具有描述性,能够清楚地表达变量的含义。命名的基本规则和大多数编程语言基本相同,有些细节上稍微有所不同。规则如下:
变量名必须以字母或下划线开头。
变量名不能以数字开头。
变量名区分大小写,推荐使用 snake_case 规则(字母全部小写和下划线)。
禁止使用和 Rust 关键字同名的变量名。
避免使用和 Rust 标准库中已有的函数、类型或模块同名的变量名。
对于私有变量 (private variables),可以使用 _ 作为前缀,区分开公共变量和私有变量。
1.1.2. 变量声明、赋值
Rust 变量声明使用 let 关键字,有些早期版本的basic语言也用LET(现在通常是DIM)。
示例:
fn main() { let x = 323; let y = 3.23; print!("x: {}, ", x); println!("y: {}", y); //输出 x: 323, y: 3.23 }
上面示例中,fn 是函数 function 的缩写,表示 main() 是这个rust程序的主函数;
let 变量名 = 常数;就是一个变量声明、赋值语句;
print!() 和 println!() 是打印的“宏”,宏不是函数,但功能也就相近于Java中的System.out.print()函数和System.out.println()函数的功能,两者输出差一个换行符。
// 表示注释语句,注释与C++相同,行注释用 //,块注释用 /* */;Rust另外还有文档注释。
函数体中每一行都是一个语句(当然语句也可以跨多行表达),语句由各种表达式组成。第一条语句必须有标点符号分号作结尾,表达式一般没有符号作结尾的。关于Rust中的“宏”,它和C/C++中的“宏”还是不同的,更多函数相关内容,放到之后的函数章节再讲。
let 语句也可以分成两行,先声明再分配值(赋值绑定):
fn main() { let x; x = 100; println!("x: {}", x); }
let 语句还可以一行赋值多个变量,但要加上括号(其实是复合数据类型之一的元组):
fn main() { let (x, y) = (3, 4); let z = (x*x+y*y) as f64; let z = z.sqrt().round() as i32; println!("{},{},{}", x, y, z); // 3,4,5 let (a, b, c) = (1, 2, 3); println!("{},{},{}", a, b, c); // 1,2,3 }
其中,as 也是Rust关键字之一,在这里用于强制转换数据类型。
sqrt()、round() 分别为平方根、取整函数。
1.1.3. Snake case
Snake case 是一种命名规范,它使用小写字母,单词之间用下划线 "_" 连接。
Rust不推荐在变量中有大写字母,示例如下:
fn main() { let Int = 100; let My_Integer = 200; let my_integer = 300; println!("{} {} {}", Int, My_Integer, my_integer) }
以上代码可以编译执行,但会有警告出现:
···Rust warning: variable `Int` should have a snake case name --> D:\Rust\hello.rs:2:9 | 2 | let Int = 100; | ^^^ help: convert the identifier to snake case (notice the capitalization): `int` | = note: `#[warn(non_snake_case)]` on by default warning: variable `My_Integer` should have a snake case name --> D:\Rust\hello.rs:3:9 | 3 | let My_Integer = 200; | ^^^^^^^^^^ help: convert the identifier to snake case: `my_integer` warning: 2 warnings emitted 100 200 300 ···
1.1.4. 禁止使用和避免使用
变量命名使用关键字,报错通不过编译,所以是禁止使用;但与标准库中已有的函数、类型同名,只是容易混淆,但编译不警告不报错,所以只能说是避免使用。如:
fn main() { //let fn = 10; //禁止使用 //let let = 2; //禁止使用 let u8 = 10u8; //避免使用 let pow; //避免使用 pow = u8.pow(2); println!("{} {}", u8, pow) //输出 10 100 }
其中, u8 的变量类型是8位无符号整型,pow() 是标准库函数平方幂函数。
函数名、类型名称作变量名不会报错,而fn, let关键字作变量名则报错:
```Rust
expected identifier, found keyword
|
help: escape `fn` to use it as an identifier
```
1.1.5 匿名变量
下划线 _ 是一个特殊的变量名,更确切地说是变量名称的缺失,就称它为匿名变量。它的基本意思是舍弃,可以理解为废纸篓,这个变量扔掉不要了,不能被再次调用。(Go, Python里也有 _ 用作匿名变量,但细节各不相同,而且在python里 “_ = 5;print(_)” 是合法的)
fn main() { let (a,_) = (6,2); println!("{}", a); //println!("{}", _); //报错^ `_` not allowed here }
1.2 基本数据类型
1.2.1 整型
Rust整型 分为有符号整型(signed,标记 in)和无符号整型(unsigned,标记 un),区别在于数字是否有负数。带符号整型的安全存储范围为 -2^(n-1) 到 2^(n-1) - 1,无符号整型的安全存储范围为 0 到 2^n,n 为数据位长度,见下表:
isize 和 usize 是根据系统架构决定的,例如带符号整型,如果系统是 64 位,类型为 i64,如果系统是 32 位,类型为 i32。(这和C++中的size_t类型相似)
指定类型和默认类型
变量声明时,可以先指定类型,再分配绑定数值,变量名后面使用冒号跟随类型来明确指定变量的类型,称为显式指定;Rust 是强类型语言,具有自动判断变量类型的能力,可理解为这是隐式指定。以下示例中声明的变量 z 并没有明确指定其具体类型,则默认为 i32 类型,这也是 Rust 的特性之一类型推断。
fn main() { let x: u8; x = 123; let y = -1000i64; let z = -323; //不指定类型,默认为i32 println!("x: {}, y: {}, z: {}", x, y, z); }
1.2.2 浮点型
Rust浮点型 分为 32 位浮点数(f32)和 64 位浮点数(f64)。浮点型若不指定具体类型,则默认为 f64 浮点数,现在的高性能CPU对两种浮点数计算的速度几乎相同,但 64 位浮点数精度更高。
fn main() { let x: f32; x = 1.23; let y: f64 = 3.23; let z = -3.23; //不指定类型,默认为f64 println!("x: {}, y: {}, z: {}", x, y, z); }
1.2.3 字符型
Rust字符型 是一个4字节 Unicode 码,支持中文等非英文字符,使用单引号''包括。
fn main() { let a = 'a'; let b: char = '字'; let c = '😊'; println!("a: {}, b: {}, c: {}", a, b, c); }
1.2.4 布尔型
Rust布尔型 用 bool 表示,占用1个字节,值只能为 true 或 false,全小写非大写字母开头。(输出与声明同形,不像C/C++只能输出1和0)
fn main() { let x = true; let y: bool = false; println!("x: {}, y: {}", x, y); //输出 x: true, y: false }
1.3 变量的可变和不可变
1.3.1 不可变变量
先看一个实例:
fn main() { let x = 6; x = x + 1; // 报错 println!("{}", x) }
在大多数编程语言中,上述实例应该打印出 7,但在Rust语言里却是报错:
```Rust error[E0384]: cannot assign twice to immutable variable `x` --> D:\Rust\hello.rs:3:5 | 2 | let x = 6; | - | | | first assignment to `x` | help: consider making this binding mutable: `mut x` 3 | x = x + 1; | ^^^^^^^^^ cannot assign twice to immutable variable ```
意思就是: 不能为不可变变量`x赋值两次`,并提示考虑绑定可变:`mut x`
特别之处: Rust变量是不可变的!
通常来说变量将是可变的,但是在 Rust 中预设变量却是不可变的。Rust 鼓励使用不可变变量,当然如果明确知道该变量是可变得,就得用关键字 mut 附加说明它是可变的变量。
1.3.2 可变变量
可变变量要用 let mut 两个关键字来声明,示例如下:
1. fn main() { 2. let mut x = 6; // 可变变量声明要在let后附加mut关键字 3. x = x + 1; 4. println!("{}", x) // 输出 7 5. }
1.3.4 静态变量(全局变量)
静态变量用 static 声明的变量的生命周期是整个程序,从启动到退出。这也是Rust中唯一的声明全局变量的方法。它的生命周期永远是'static, 它占用的内存空间也不会在执行过程中回收。
注意点:
1. 全局变量必须在声明的时候马上初始化;
2. 命名时字母要全部大写,否则编译有警告信息;
3. 全局变量的初始化必须是编译期就能确定的常量;
4. 带有 mut 修饰的全局变量,在使用的时候必须使用 unsafe 关键字;
5. 全局变量可以写到函数外面,被任意一个函数使用。
fn main() { static G1 : i32 = 3; println!("{}, {}", G1, G1 + 20); static mut G2 : i32 = 4; unsafe { // G1 = 2; // cannot assign to immutable static item `G1` G2 = G1 + 5; println!("{}, {}", G1, G2); } } /* 输出 3, 23 3, 8 */
1.4 常量
常量,和变量一样都是用来存储数据的标识符,常量使用 const
关键字声明。常量的值是不可变的,不允许使用mut关键字修饰这个变量绑定。
1.4.1 常量命名
常量的命名规则和方法与静态变量基本类似,命名时字母也要全部大写。
错误示例:常量字母不全部大写可以通过编译,但有警告信息。
const pi:f32 = 3.14159; //报错 const Pi:f32 = 3.14159; //报错 fn main() { println!("{}", pi); println!("{}", Pi); }
``` warning: constant `pi` should have an upper case name --> D:\Cpp\hello.rs:1:7 | 1 | const pi:f32 = 3.14159; | ^^ help: convert the identifier to upper case: `PI` | = note: `#[warn(non_upper_case_globals)]` on by default warning: constant `Pi` should have an upper case name --> D:\Cpp\hello.rs:2:7 | 2 | const Pi:f32 = 3.14159; | ^^ help: convert the identifier to upper case (notice the capitalization): `PI` warning: 2 warnings emitted 3.14159 3.14159 ```
1.4.2 常量用法
常量必须显式指定类型以及声明同时就赋值,并且常量的值必须是编译时可确定的常数或者是常量表达式。示例如下:
const PI: f64 = 3.14159; const FLAG: bool = true; const HOURS: u32 = 12; const SECONDS: u32 = 60 * 60 * HOURS; const MIN_NUM: i32 = 1 << 31; const MAX_NUM: i32 = -(1 + MIN_NUM); fn main() { println!("{}", PI); println!("{}", FLAG); println!("{}", HOURS); println!("{}", SECONDS); println!("{}", MIN_NUM); println!("{}", MAX_NUM); } /* 输出: 3.14159 true 12 43200 -2147483648 2147483647 */
错误示例:常量不可以隐式赋值,或者先声明后赋值。
const PI = 3.14159; //报错 const FLAG:bool; //报错 fn main() { FLAG = true; //报错 println!("{}", PI); println!("{}", FLAG); }
``` error: free constant item without body --> D:\Cpp\hello.rs:2:1 | 2 | const FLAG:bool; | ^^^^^^^^^^^^^^^- | | | help: provide a definition for the constant: `= <expr>;` error: missing type for `const` item --> D:\Cpp\hello.rs:1:9 | 1 | const PI = 3.14159; | ^ help: provide a type for the constant: `: f64` error[E0070]: invalid left-hand side of assignment --> D:\Cpp\hello.rs:5:10 | 5 | FLAG = true; | ---- ^ | | | cannot assign to this expression error: aborting due to 3 previous errors For more information about this error, try `rustc --explain E0070`. ```
1.4.3 标准库常量
引用标准库中的常量,使用关键字 use 来导入。(类似 java和python 中 import)
比如std::f32或std::f64中的一些数学常量:
use std::f32::consts::PI; use std::f32::consts::FRAC_1_PI; use std::f32::consts::FRAC_2_PI; use std::f32::consts::SQRT_2; use std::f32::consts::FRAC_1_SQRT_2; use std::f32::consts::E; use std::f32::consts::LN_2; use std::f32::consts::LOG2_E; use std::f32::consts::LOG10_E; fn main() { println!("{}", PI); println!("{}", FRAC_1_PI); println!("{}", FRAC_2_PI); println!("{}", SQRT_2); println!("{}", FRAC_1_SQRT_2); println!("{}", E); println!("{}", LN_2); println!("{}", LOG2_E); println!("{}", LOG10_E); } /* 输出 3.1415927 0.31830987 0.63661975 1.4142135 0.70710677 2.7182817 0.6931472 1.442695 0.4342945 改std::f64,则输出 3.141592653589793 0.3183098861837907 0.6366197723675814 1.4142135623730951 0.7071067811865476 2.718281828459045 0.6931471805599453 1.4426950408889634 0.4342944819032518 */
1.5 本章小结
1. 变量(Variables):在 Rust 中,变量默认是不可变的(immutable),也就是说,一旦被赋值后,就不能再修改其值。如果需要修改变量的值,需要使用 `mut` 关键字来声明可变变量。
2. 常量(Constants):与变量不同,常量在声明后就不能再修改其值。在 Rust 中,使用 `const` 关键字来声明常量,常量的命名规则与变量相同,但必须使用大写字母命名,并且必须在声明时就赋值。
3. 可变绑定(Mutable bindings):除了使用 `mut` 关键字来声明可变变量外,还可以使用 `let` 关键字来声明一个可变绑定。可变绑定允许我们在不改变绑定本身的情况下修改绑定所指向的值。
4. 类型推断(Type inference):Rust 支持类型推断,也就是说,可以在声明变量或常量时省略类型,由编译器自动推断类型。例如,可以使用 `let x = 42;` 来声明一个整数变量,编译器会自动推断出 x 的类型为 i32。
5. 变量遮蔽(Variable shadowing):Rust 中可以使用相同的名称来声明一个新的变量或常量,这会遮蔽之前的变量或常量。这个特性可以用来在不改变原有代码的情况下修改变量的值或类型。
6. 变量的作用域由声明的位置开始,直到当前作用域的结束;变量在离开作用域后会被自动销毁。常量在整个程序运行期间都存在。
注:本文中所有代码都可以在Rust官方线上编译器里编译通过。