Rust 基础
Rust 是个基于表达式 以及函数式编程的语言
use
- 这个东西就等于是
JS中的import、require这种的引入包,插件的语法。
std 在标准库中 默认情况下 rust 会把一个叫 prelude(也叫预导入) 这个模块的内容导入每个程序的作用域中,它也是 标准库 的一部分
如果我们要使用的类型不在这个 prelude 里,那么就必须要使用 use 这个关键字把这个类型显示的导入到程序中。
use std::io;
rand 是非标准库 也叫 trait ,可以看作是 JS 里面的 API
use rand::Rng;
let 、 mut
在 Rust 里面所有的变量默认都是不可变的 叫 immuteble ,在 JS 里面我们为了提高对象的性能会使用一个库叫 immuteble.js 来使对象编程是不可变对象的道理是类似的
- 相同:
let就跟JS里面的一样,但是在Rust这门语言里,不加上mut修饰符来修饰就等于是JS里面的const但不是Rust中的常量。 - 不同: 变量可覆盖,
JS中letconst不可重新定义,var可以 但这个概念叫shadowing。
function main() {
let name = "ACE";
name = "Protgas.D.ACE";
}
fn main() {
let mut name = "ACE";
println!("Welcome, {} to the Rust world!", name);
name = "Protgas.D.ACE";
println!("Welcome, {} to the Rust world!", name);
}
:: 这个像 CSS 里面伪元素的东西到底是个啥呢?
这个 :: 是 关联函数,模块创建的命名空间
// 例如
use std::io // 引用 IO 库
let mut name = String::new(); // 返回字符串的一个新的实例
rust 里 字符串就是 String 这是标准库提供的,内部使用了 UTF8 格式的编码,并且可以按照需求进行扩展自己的大小,:: 表明这个 new 是 String 类型的一个 关联函数,
关联函数 就是针对类型本身来实现的,而不是针对字符串的某个特定实例来实现的。
关联函数:就等于是 C# 或者是 Java 语言中的的静态方法一样, new 函数就等是一个空白的字符串,在 rust 里很多类型也有 new 函数,
在 rust 里面 new 这个名是创建类型实例的一个惯用函数名,所以这句话就是定义了一个可变的变量 name ,然后把它的值绑定到了一个空字符实例上面。
& 取地址符号
这个 “并且” 符号是个啥玩意呢?
std::io::stdin().read_line(&mut name).expect("这个啥错了");
& 表示这个 实参 是一个 引用(reference),它允许多处代码访问同一处数据,而无需在内存中多次拷贝。
注意:引用在 Rust 里是一个复杂的特性, Rust 的一个主要优势就是安全而简单的操纵引用。它就像 JS 里面的 const 默认不可改。
& 就表示引用:允许你引用某些值而不取得所有权,引用在 rust 里面也是默认不可变的,必须要配合 mut 关键字让这个引用也变成可变的。
IO 库有个 stdin stdin 返回类型叫 Stdin 的实例, stdin 会被用做句柄来处理终端中的标准输入。
我们把引用作为函数参数的这个行为叫做借用,并且只读。
在特定的作用域内,对于某一块数据,只能有一个可变的引用。
这样做的好处就是可在编译的时候防止数据竞争。
一下三种行为下会发生数据竞争
- 超过 1 个指针同时访问同一块数据
- 至少有一个指针用于写入数据
- 没有使用任何机制来同步对数据的访问
可以通过创建新的作用域,来允许非同时的创建多个可变引用
fn main() {
let mut s = String::from("hello world");
{
let s1 = &mut s;
}
let s2 = &mut s;
}
constant 常量
/**
* 跟 JS 中常量的区别
* - 大写 不然警告
* - 可重复定义
* 跟 Rust 中 let 的区别
* - 不能 mut
* - 可以全局定义
* - 必须定义类型
*/
const MAX: u32 = 100_000; // 数字可以增加下划线增加可读性
shadowing 隐藏
可以重复声明变量
fn main() {
let x = 2;
let x = x + 1;
let x = x * 2;
// 猜猜等于啥
println!("{}", x);
}
shadowing 可以被任意类型重新赋值。
mut 只可以被相同类型赋值。
数据类型
标量 和 复合类型
Rust是静态编译语言,在编译时必须知道所有变量的类型。- 基于使用的值,编译器通常能够推断出它的具体类型
- 但如果可能的类型比较多(例如把
String转为整数的parse方法),就必须添加类型的标注,否则编译就会报错。
fn main(){
let love:u32 = "520".parse().expect("这不是个数字");
println!("{}", love);
}
基于上面的案例 Rust 有很多的整数数据类型,所以要讲 love 这个变量具体的指明是什么类型,这样 parse 方法就知道应该把 "520" 这个字符串解析成什么类型了,如果不标注成什么类型
那么 love 就会报错,并且编译也会报错,因为 Rust 的 parse 方法 和 编译器不知道把 "520" 这个字符串字面值解析成哪种的整数类型。
fn main(){
let love = "520".parse().expect("这不是个数字");
println!("{}", love);
}
标量类型
- 一个标量类型代表一个单个的值。
Rust有四个主要的标量类型。
整数类型
- 没有小数部分
- 例如 u32 就是一个无符号的整数类型,占据 32 位的空间。
- 无符号整数类型以 u 开头,u 是 unsigned 的意思。
- 有符号整数类型以 i 开头。
- Rust 的整数类型对照表如图
- 每种都分为 i 和 u , 以及固定的位数。
- 有符号范围:
负 2 的 n 次方减 1到2 的 n 减 1 次方减 1;
- 无符号范围:
0 到 2 的 n 次方减 1
| Length | Signed | Unsigned |
|---|---|---|
| 8-bit | i8 | u8 |
| 16-bit | i16 | u16 |
| 32-bit | i32 | u32 |
| 64-bit | i64 | u64 |
| 128-bit | i128 | u128 |
| arch | isize | usize |
arch:系统架构 isize usize 根据系统位数决定 如果是 32 位 分别就是 i32 u32
整数字面值
| Number Literals | Example |
|---|---|
| Decimal | 98_222 |
| Hex | 0xff |
| Octal | 0o77 |
| Binary | 0b1111_0000 |
| Byte(u8 only) | b'A' |
- Decimal: 10 进制 可以加 _增加可读性
- Hex: 16 进制 0x 开头
- Octal: 8 进制 0o 开头
- Binary: 2 进制 0b 开头 可以加_ 增加可读性
- Byte: 仅限 u8 类型 b 开头跟着一个字符
- 除了
Byte,所有的数值字面值都允许使用类型后缀。 例如27u8。如果不确定哪种类型,可以使用整数默认的类型i32:- 总体上来说速度很快,即使在 64 位的系统中。
无符号整数类型包括以下几种:
- u8,占用 8 位(即 1 个字节),范围在 [0, 255] 之间;
- u16,占用 16 位(即 2 个字节),范围在 [0, 2^16] 之间;
- u32,占用 32 位(即 4 个字节),范围在 [0, 2^32] 之间;
- u64,占用 64 位(即 8 个字节),范围在 [0, 2^64] 之间;
- u128,占用 128 位(即 16 个字节),范围在 [0, 2^128] 之间;
- usize,根据平台决定存储位数,在 32 位平台下占用 32 位,在 64 位平台下占用 64 位;
有符号整数类型包括以下几种:
- i8,占用 8 位(即 1 个字节),范围在 [-128, 127] 之间,相当于 Java 中的 byte 类型;
- i16,占用 16 位(即 2 个字节),范围在 [-2^15, 2^15 - 1] 之间,相当于 Java 中的 short 类型;
- i32,占用 32 位(即 4 个字节),范围在 [-2^31, 2^31 - 1] 之间,相当于 Java 中的 int 类型;
- i64,占用 64 位(即 8 个字节),范围在 [-2^63, 2^63 - 1] 之间,相当于 Java 中的 long 类型;
- i128,占用 128 位(即 16 个字节),范围在 [-2^127, 2^127 - 1] 之间;
- isize,根据平台决定存储位数,在 32 位平台下占用 32 位,在 64 位平台下占用 64 位;
整数溢出
- 例如:
u8的范围是0-255,如果把一个u8变量的值设为256,那么就会报错:- 调试模式下编译:
Rust会检查整数溢出,如果发生溢出,程序在运行时就会panic(恐慌)。 - 发布模式下(--release)编译:
Rust不会检查可能导致的panic的整数溢出。- 如果溢出发生:
Rust会执行 “环绕” 操作:- 256 变成 0 , 257 变成 1...
- 但程序不会
panic。
- 如果溢出发生:
- 调试模式下编译:
the literal '256' does not fit into the type 'u8' whose range is '0..=255'
浮点
f32,单精度浮点数,占用 32 位(即 4 个字节),相当于Java中的float类型;f64,双精度浮点数,占用 64 位(即 8 个字节),相当于Java中的double类型;
Rust 的浮点类型使用了 IEEE-754 标准来描述。默认类型是 f64。
f64 是默认类型,因为在现代 CPU 上 f64 和 f32 的速度差不多,而且精度更高。
数值操作
加减乘除余
fn main(){
let sum = 1 + 2;
let difference = 9.5 - 4.1;
let product = 2 * 25;
let quotient = 25.7 / 12.2;
let reminder = 32 % 5;
}
布尔 bool
占 1 个字节
字符类型
Rust语言中char类型被用来描述语言中最基础的单个字符。- 字符类型的字面值使用单引号。
- 字面值使用单引号 占 4 个字节 是 Unicode 标量值,可以表示比 ASCII 多得多的字符内容:拼音、中日韩文、零长度空白字符、emoji 表情等。
- U+0000 到 U+D7FF
- U+E000 到 U+10FFFF
Unicode 并没有字符的概念,所以直接上认识的字符也许跟 Rust 中的概念并不相符。
复合类型
可以将多个值放到一个类型里面
Rust 提供了两种基础的复合类型:元祖(Tuple)、数组
元组 (Tuple)
Tuple可以将多个类型的多个值放在一个类型里Tuple的长度固定:一旦声明就无法改变
Rust 里面的元组跟 JS 里面的区别是这样的
const turple = ["ACE", "Luffy", "Zoro", "Nami", ...]
// 结构赋值 destructor
const [a, b, c, d] = turple;
let turple: (i32, f64, u8) = (520, 13.14, 1);
// 解构赋值
let (x, y, z) = turple;
println!("{},{},{}", x, y, z);
println!("{},{},{}", turple.0, turple.1, turple.2);
数组
Array多个值放在一个类型里Array每个元素类型必须保持一致Array的长度固定:一旦声明就无法改变
如果想让你的数据存放在栈上,不是在堆上,或者想保证有固定数量的元素,这时使用数组更有好处
let array: [&str; 5] = ["路飞", "索隆", "娜美", "乌索普", "山治"];
let [a, b, c, d, e] = array;
println!("{},{},{},{},{}", a, b, c, d, e);
let same_array = ["路飞"; 5];
println!("{:?}", same_array);
JS元祖就是数组,数组也是元祖,这样理解就可以了,Rust元祖和数组是不一样的,元祖的元素可以由任意类型组成,数组必须统一类型。JS的数组可以理解为不限制长度2^32,不需要定义长度,Rust必须定义长度。JS的数组是在堆内存,Rust的数组是在栈上分配的单个块的内存。
Array 是由 prelude 模块提供的 Vector是由 标准库 提供的,并且更灵活,长度可以改变
访问数组的元素
- 数组是 Stack 上分配的单个块的内存
- 可以使用索引来访问数组的元素(例子)
let months = ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"];
let first = months[0]
- 如果访问的索引超出了数组的范围,那么:
- 编译会通过
- 运行时报错(runtime 发生 panic)
Rust不允许其继续访问响应地址的内存。
let first = months[12];
println!("{}", first);
error: this operation will panic at runtime
--> src\main.rs:4:17
|
4 | let first = months[12];
| ^^^^^^^^^^ index out of bounds: the length is 12 but the index is 12
|
= note: `#[deny(unconditional_panic)]` on by default
error: could not compile `lesson5` due to previous error
函数
- JS 没有规范,可以任意的命名。 Rust 函数名必须小写 ,多个单词用 _ 隔开 并且 fn 关键字
- 定义返回值类型是在单箭头的后面定义
- 最后一句表达式不加分号就不用 return 关键字。
fn demo(x: i32) -> i32 { // parameter
1
}
demo(5) // arguments