介于偏底层的系统语言(就像 C 或 C++ 语言)和基于运行时的语言(比如 Java 或 Python)之间,但更适用于系统编程领域。它是系统编程语言,系统编程语言说明它有直接操作硬件的能力,就像C/C++一样。(几乎)拥有对硬件的整体控制(内存布局,处理器功能)。相比之下Go 甚至不是系统编程语言,尽管使用它在后端基础架构上编写微服务和工具等非常棒,但我不希望使用它编写内核或内存分配器。
内存安全(memory-safe)。安全应对空指针、竞态条件和各种低级威胁。在C/C++里面,很容易会因为内存管理的问题而出现Segmentation Fault,在Rust中,由于引入了所有权(Ownership)和生存期(Lifetime)的概念,实现了自动内存管理的同时,还避免了各种内存错误。但同时,也提供unsafe块和祼指针(Raw Pointer),提供更多的自由度。可预测的运行时行为(零代价抽象 zero cost abstractions,无垃圾回收),free of charge。相比之下Go为了保证语言的简洁性和正交性,将很多底层的操作推迟到运行时来进行。
不使用 GC 使 Rust 奇快无比,特别是在需要保证延迟,而不仅仅是高吞吐量的时候
高性能高并发
具备更简单的多线程模型,类似于 C++ 或 Java。与go相比,具有更好的线程间通信能力,比如 MPSC channel(非常类似于 Go channel)
工具:
Rust 的 相当出色,完善的文档,并且可以轻松扩展子命令。
高阶函数(闭包,Closure),C++11 之后也提供了Lambda来支持闭包,但在C中还没有。
模式匹配(Pattern Match)和ADT(Algebraic Data Type),在C/C++中都没有提供。模式匹配和ADT在许多函数式编程语言中都作为标准来提供了。有了ADT,可以把许多操作都统一起来,并且利用模式匹配可以很方便地把其中的数据取出来。
// 引用标准库
// 所有的系统库模块都是被默认导入的,所以在使用的时候只需要使用 use 关键字简化路径就可以方便的使用了。
use std::f64::consts::PI;
// use 关键字能够将模块标识符引入当前作用域并设置别名
use crate::nation::govern as nation_govern;
// use 关键字可以与 pub 关键字配合使用
// 例子1
let name = "Pascal".to_string();
let a = name;
let b = name;
// 在其他语言比如js中,可能会认为a和b都有一个对name的引用并且它们都指向相同的数据
// 但在rust中会报错,因为把name赋值给b的时候,name实际上已经不再拥有值了。为什么呢?因为在这个时候,所有权已经被移动给a了
// 额外很重要的一点是,所有的这种静态分析都是由编译器完成,而实际上并没有运行我们的代码。
// 例子2 也是一样
fn greet(name: String) {
println!("Hello, {}!", name);
}
let name = "Pascal".to_string();
greet(name);
greet(name); // Move happened earlier so this won't compile
// 例子3 就不会像上面那样,因为name变成了&str类型
let name = "Pascal";
let a = name;
let b = name;
// 例子1 比如你只想打印值,而不想改变值的所有权
fn greet(name: &String) {
println!("Hello, {}!", name);
}
let name = "Pascal".to_string();
greet(&name);
greet(&name); // 可以多次借用
// 例子2
let name = "Pascal".to_string();
let a = &name;
let b = a;
1 工具生态(Tooling Ecosystem)
Version Manager:rustup -- the Rust installer and version management tool
更新rustrustup update
本地查看文档rustup doc
Package:In Rust, we often refer to packages as “crates.”
PackageManager:Cargo
create your project with cargo new projectNameA
格式化cargo fmt
修复代码警告cargo fix
build your project with cargo build
run your project with cargo run
test your project with cargo test
build documentation for your project with cargo doc
// 简单例子
let a = 123;
// 有三个错误的写法和一个正确的写法
a = "abc"; // incorrect,a已被确定为整型数字,不能把字符串类型的值赋给它
a = 4.56; // incorrect,这个自动转换数字精度有损失,Rust 语言不允许精度有损失的自动数据类型转换(待确认)
a = 456; // incorrect,a不是个可变变量
let a = 456.2 ; // correct, Shadowing
let mut a = "hello" ; // correct, Shadowing
// Shadowing
let mut a = Vec::new();
let a = a; // a从可变变量变成不可变变量了
可变变量:使用mut(英文mutable的简写)来声明,它仅仅是值可以变化。
let mut a = 123;
a = 456;
let a = "hello"; // correct, Shadowing
// 字面量
let hello: &'static str = "Hello, world!"; // They are 'static because they’re stored directly in the final binary, and so will be valid for the 'static duration.
let hello = "Hello, world!"; // "Hello, world!"本身是str类型,hello变量是&str类型
// 1. 声明String类型,使用"xxx".to_string()或者String::from("xxx")
let s = "Have a nice day".to_string();
let mut s = "Have a nice day".to_string();
let b = String::from("tom");
// 追加文本
s.push_str( " Precht");
Rust为什么同时具有String和&str:安全性,正确性和性能。(待整理)
布尔
整型(Integer)
声明
// 不指定类型的情况下默认会被推导为32位整型
let a = 123;
let a: u64 = 123; // 指定类型
// 字面量后面可以跟后缀,代表数字的具体类型
let var1 = 123usize; // 1变量是usize类型
let var2 = 0x_ff_u8; // 2变量是u8类型
let var3 = 32; // 整数不写类型,默认为 i32
let var4 = 100i64; // i64
// 1. 声明
// 普通声明
enum Book {
Papery,
Electronic,
}
let book = Book::Papery;
println!("{}", book);
// 元组字段声明
enum Book {
Papery(u32),
Electronic(String),
}
let book = Book::Papery(1001);
// 结构体字段声明
enum Book {
Papery { index: u32 },
Electronic { url: String },
}
let book = Book::Papery{index: 1001};
// 2. 判断和使用枚举
// 不能直接使用枚举,比如book就不能直接使用,需要结合match使用
match book {
Book::Papery { index } => {
println!("Papery book {}", index);
},
Book::Electronic { url } => {
println!("E-book {}", url);
}
}
元组
元组用一对()包括的一组数据来表示,可以包含不同种类的数据
// 1. 元组的声明
// 0个元素,此时称为unit tuple(单元元组),占用0内存空间
let a = ();
// 单个元素
let a = (0);
let a = (0,);
// 多个元素
let tup: (i32, f64, u8) = (500, 6.4, 1);
println!("{}",tup.0); // 500
let (x, y, z) = tup; // 可以用js解构的方式获取其中的数据
println!("{}",y); // 500
// 不显式声明类型
let a = (2,3);
// 嵌套
let a = ("b", (1i32, 2i32));
// 2. 获取元组中的元素,有两种方式
// 数字索引
let tup: (i32, f64, u8) = (500, 6.4, 1);
println!("{}",tup.0); // 500
// 模式匹配, 类似js解构的方式
let (x, y, z) = tup;
println!("{}",y); // 500
数组
数组用一对[ ]包括的同类型数据来表示
// 声明
let a = [1, 2, 3, 4, 5];
let c: [i32; 5] = [1, 2, 3, 4, 5]; // c 是一个长度为 5 的 i32 数组
let d = [3; 5]; // 等同于 let d = [3, 3, 3, 3, 3];
a[0] = 123; // 错误:数组 a 不可变
let mut a = [1, 2, 3];
a[0] = 4; // 正确
// 在函数中传递数组
fn single_nubmer(arr: [i32; 7]) -> i32 {
let mut res = 0;
for e in arr.iter() {
res = res ^ e;
}
return res;
}
使用:
遍历
// 1.
let a = [10, 20, 30, 40, 50];
let mut index = 0;
while index < 5 {
println!("the value is: {}", a[index]);
index += 1;
}
// 2.
let vec = vec!['a', 'b', 'c'];
for i in 0..vec.len() {
println!("{}", vec[i]);
}
// 3.
let vec = vec!['a', 'b', 'c'];
for i in vec {
println!("{}", i);
}
// 4.
for element in a.iter() {
println!("the value is: {}", element);
}
// 声明
let vector = vec![1, 2, 4, 8]; // 通过数组创建向量
let vector: Vec<i32> = Vec::new(); // 创建类型为 i32 的空向量
// 遍历
for num in vector {
// do something
}
// 取出向量中的值
v[1]); // 不安全
v.get(0); // get是一种安全的取值方法,返回值是 Option 枚举类,有可能为空
// 追加单个元素
vector.push(16);
// 拼接两个向量
v1.append(&mut v2);
切片(slice)
rust的切片一定是引用类型,写法是&T[x..y],T是某个线性数据结构的变量名
// 基于字符串切片
let s = String::from("broadcast");
let part1 = &s[0..5];
let part2 = &s[5..9];
println!("{}={}+{}", s, part1, part2);
// 基于数组切片
let arr = [1, 3, 5, 7, 9];
let part = &arr[0..3];
let part2 = &arr;
// 在函数中使用切片
fn single_nubmer_slice(slice: &[i32]) -> i32 {
let mut res = 0;
for e in slice.iter() {
res = res ^ e;
}
return res;
}