序章
前言
发生了很多我意料之外的事情,我也因此感慨万分
虽然其中的道理我早有耳闻,但是切身体会后内心还是会泛起伤感
在此之后我不免对开源产生了怀疑——我为什么要开源?
这是我之后需要思考的问题,而现在则是认真地学习Rust
更新日志
2022/02/20
这次准备对官方文档做摘要了
概述
发展
特性
Rust 语言为了高并发安全而做的设计
基础语法
变量和常量
按照做摘要的原则,我会过滤掉那些过于简单和相对熟悉的内容,但是Rust的变量实在是太特别了
不可变变量
Rust是强数据类型语言,但是能够做自动类型判断,变量声明语法如下:
let a = 123;
再执行上述语句后,下面这些代码将会报错
a = "abc"; // 类型发生了转变
a = 4.56; // 精度发生了损失
a = 456; // Rust的奇妙特性
重点解释第三种情况——要知道a是一个变量,要是不能改变那不就是常量了吗?
a 的值不可变。但这不意味着 a 不是”变量”(英文中的 variable),官方文档称 a 这种变量为**”不可变变量”**(就离谱)
这就牵扯到了 Rust 语言为了高并发安全而做的设计:在语言层面尽量少的让变量的值可以改变
但是以下代码是合法的
let a = 123;
let a = 456;
这样相当于“重新绑定”了,这也是不可变量和常量的区别
当然,”重新绑定”只能改变值而不能改变数据类型
这里的”重新绑定”严格来说是指的重影,这个概念后面再提
可变变量
当然,上面这种机制在很多情况下会比较难受,所以还是需要我们熟悉的正常变量
let mut a = 123;
a = 456;
常量
语法如下:
const a = 123; // 有符号三十二位
数据类型
只讨论较为特殊的
虽然能自动判别数据类型,但是依旧是可以指定数据类型,语法类似TS
let a: u64 = 123; // 无符号64位
const a: i32 = 123; // 有符号32位
如果不指定类型,那么对于数字声明来说默认是有符号32位
整数型
Integer,简称整形,按照比特长度和有无负号分类
位长度 | 有符号 | 无符号 |
---|---|---|
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 两种整数类型是用来衡量数据大小的,它们的位长度取决于所运行的目标平台,如果是 32 位架构的处理器将使用 32 位位长度整型。
进制 | 例 |
---|---|
十进制 | 98_222 |
十六进制 | 0xff |
八进制 | 0o77 |
二进制 | 0b1111_0000 |
字节(只能表示 u8 型) | b’A’ |
浮点数型
Floating - Point,
只需要记住有32位和64位,默认是64位即可
fn main() {
let x = 2.0; // f64
let y: f32 = 3.0; // f32
}
字符型
char,但是与C/C++不同的是,rust的char是4个字节,代表Unicode标量值,这意味着中文日文甚至是emoji表情符号在rust中都是有效的字符
当然,有时候我们对字符的认识可能会和rust不一致,并且由于现行的中文编码有GBK和UTF-8两套,所以中文仍可能会出现乱码
为了尽可能规避乱码,UTF-8字符尽可能使用字符串存储
数组
大体上和JS类似,主要是看一段示例:
let b = ["January", "February", "March"];
// b 是一个长度为 3 的字符串数组
let c: [i32; 5] = [1, 2, 3, 4, 5];
// c 是一个长度为 5 的 i32 数组
let d = [3; 5];
// 等同于 let d = [3, 3, 3, 3, 3];
let first = a[0];
let second = a[1];
// 数组访问
a[0] = 123; // 错误:数组 a 不可变
let mut a = [1, 2, 3];
a[0] = 4; // 正确
复合类型
元组用一对()包括一组数据,可以包含不同类型的数据
这里和ES6中的解构赋值类似之处
let tup: (i32, f64, u8) = (500, 6.4, 1);
// tup.0 等于 500
// tup.1 等于 6.4
// tup.2 等于 1
let (x, y, z) = tup;
// y 等于 6.4
重影
重影的概念与其他面向对象语言里的”重写”(Override)或”重载”(Overload)是不一样的。重影就是刚才讲述的所谓”重新绑定”,之所以加引号就是为了在没有介绍这个概念的时候代替一下概念
fn main() {
let x = 5;
let x = x + 1;
let x = x * 2;
println!("The value of x is: {}", x);
}
运行结果
The value of x is: 12
重影是指用同一个名字重新代表另一个变量实体,其类型、可变属性和值都可以变化。但可变变量赋值仅能发生值的变化,数据类型是不允许变化的!
注释
rust有常规的两种注释形式,还有非常特殊的第三种注释:
/// Adds one to the number given.
///
/// # Examples
///
/// ```
/// let x = add(1, 2);
///
/// ```
这样的注释支持markdown语法,是作为说明文档注释的开头
能达到下面这样的效果,非常惊艳!
函数
主函数
和C/C++类似,rust需要一个主函数,其基本格式如下:
fn main () {
println!("Hello World!");
}
没有参数,也没有返回值!
自定义函数
基本使用
和JS一样,函数声明自动提升,所以不必在意调用和声明的先后顺序
但是需要注意,rust中声明函数需要指定类型,语法类似TS
fn another_function(x: i32, y: i32) {
println!("x 的值为 : {}", x);
println!("y 的值为 : {}", y);
}
函数体表达式
大概是个语法糖
fn main() {
let x = 5;
let y = {
let x = 3;
x + 1
};
println!("x 的值为 : {}", x);
println!("y 的值为 : {}", y);
}
最后输出的结果是:
x 的值为 : 5
y 的值为 : 4
没错,不仅没有输出花括号,而且y的值是4
没有输出花括号是因为rust中的花括号可以理解为c中的%d%c%s等等
可以将函数的形式简写成上述代码中的那样,其中最后一句必须是表达式 且 不加分号,作为返回值(最后一行加上分号则会变成一条语句)
而且如果要使用这种简写,那么一定要放到最后一行!!
当然,return是随时都可以使用
返回值类型声明
如果要限定返回值的类型,那么就可以使用如下形式:
fn add(a: i32, b: i32) -> i32 {
return a + b;
}
在->后加上返回值类型即可声明
条件语句
如果是耳熟能详的内容,那我绝不会在这里大费周章
基本使用
和python类似,这里不再需要括号(但是依然可以写,因为括号可以看做表达式的一部分)
而且条件表达式必须是bool类型
if a > 0 {
b = 1;
}
else if a < 0 {
b = -1;
}
else {
b = 0;
}
由于rust是注重安全的,所以非0即true这种自动类型转换时不被允许的
结合函数体表达式
这样的效果和三目运算差不多
let number = if a > 0 { 1 } else { -1 };
两个函数体表达式的类型必须一样!且必须有一个 else 及其后的表达式块
(因为赋值操作的等号右边必须要有一个返回值)
循环
while循环
while只提两点:
- 不需要括号
- 暂时没有do-while,do作为了保留字
for循环
没有像C那样的三元语句控制循环,而是用迭代器的形式遍历
for i in a.iter() {
println!("值为 : {}", i);
}
当然还可以通过下标来访问:
fn main() {
let a = [10, 20, 30, 40, 50];
for i in 0..5 {
println!("a[{}] = {}", i, a[i]);
}
}
看上去有点粗陋就是了额
loop循环
这个相当于加强版的while(true){}
let s = ['R', 'U', 'N', 'O', 'O', 'B'];
let mut i = 0;
let location = loop { // 没错loop循环有一个类似返回值的操作
let ch = s[i];
if ch == 'O' {
break i; // 可以通过break做到类似返回值的效果
}
i += 1;
};