Rust 一门赋予每个人构建可靠且高效软件能力的语言https://hannyang.blog.csdn.net/article/details/130467813?spm=1001.2014.3001.5502关于Rust安装等内容请参考上文链接,写完上文就在考虑写点关于Rust的入门文章,本专辑将直接从Rust基础入门内容开始讲起。标题《快速入门60分》并不指60分钟,而是希望自己写完这个专辑后,可以得个60分,也能掌握Rust60%上下的内容,请看第一章《变量与常量》:
目录
一、变量与常量
1.1 变量
1.1.1. 变量命名
1.1.2. 变量声明、赋值
1.1.3. Snake case
1.1.4. 禁止使用和避免使用
1.1.5 匿名变量
1.2 基本数据类型
1.2.1 整型
1.2.2 浮点型
1.2.3 字符型
1.2.4 布尔型
1.3 变量的可变和不可变
1.3.1 不可变变量
1.3.2 可变变量
1.3.3 变量遮蔽
1.3.4 静态变量(全局变量)
1.4 常量
1.4.1 常量命名
1.4.2 常量用法
1.4.3 标准库常量
1.5 本章小结
一、变量与常量
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:Rusthello.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:Rusthello.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 为数据位长度,见下表:
位长度 | 有符号 | 无符号 |
---|---|---|
8-bit | i8 | u8 |
16-bit | i16 | u16 |
32-bit | i32 | u32 |
64-bit | i64 | u64 |
128-bit | i128 | u128 |
arch 由系统构架而定 |
isize | usize |
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:Rusthello.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 两个关键字来声明,示例如下:
fn main() {
let mut x = 6; // 可变变量声明要在let后附加mut关键字
x = x + 1;
println!("{}", x) // 输出 7
}
1.3.3 变量遮蔽
Rust可以定义一个与之前变量同名的新变量,称之为第一个变量被第二个 Shadowing 了,官方用的这个词 Shadowing,有时被翻译成隐藏有时被翻译成遮蔽,还有的翻译为重影。它的意思就是当使用相同变量的名称时,编译器将“看到”第二个变量。实际上,第二个变量 “遮蔽” 了第一个变量,此时任何使用该变量名的行为中都会视为是在使用第二个变量,直到第二个变量自己也被隐藏或第二个变量的作用域结束。可以用相同变量名称来隐藏一个变量,以及重复使用 let 关键字来多次隐藏。总之,它和可变变量不是同一个概念。
fn main() {
let x = 6;
let x = x + 1;
println!("{}", x); // 7
let x = x * x;
println!("{}", x); // 49
}
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:Cpphello.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:Cpphello.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
错误示例:常量不可以隐式赋值,或者先声明后赋值。
const PI = 3.14159; //报错
const FLAG:bool; //报错
fn main() {
FLAG = true; //报错
println!("{}", PI);
println!("{}", FLAG);
}
“`
error: free constant item without body
–> D:Cpphello.rs:2:1
|
2 | const FLAG:bool;
| ^^^^^^^^^^^^^^^-
| |
| help: provide a definition for the constant: `= ;`
error: missing type for `const` item
–> D:Cpphello.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:Cpphello.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官方线上编译器里编译通过。
Rust PlaygroundA browser interface to the Rust compiler to experiment with the languagehttps://play.rust-lang.org/?version=stable&mode=debug&edition=2021作者水平有限,如有不当之处,敬请指正。
本章完