Rust 基础入门
1.变量绑定与解析
- 变量默认绑定,使用
mut
手动控制可变 - 使用下划线开头定义变量忽略未使用的变量
- 变量解析
let (a, mut b): (bool,bool) = (true, false);
- 常量定义
const MAX_POINTS
: u32 = 100_000 # 常量的命名约定是全部字母都使用大写,并使用下划线单词,另外对数字字面量可插入下划线以提高可读性): - 变量遮蔽
let x = 5; let x = x + 1;
// 后面的x遮蔽前面的x,涉及一次内存对象的再分配,不在一个地址上let spaces = " "; let spaces = spaces.len();
// 第二个是一个全新的变量,只是变量名相同- 如果这样就是不允许的
spaces = spaces.len();
2.基本类型
- 能返回值就是表达式
- 函数 永不返回,永不跳出
!
永不返回fn forever() -> ! { loop { //... }; }
3.所有权和借用
- 1.rust中每一个值都被一个变量所拥有,该变量被称为值的所有者
- 2.一个值同时只能被一个变量所拥有,或者说一个值只能拥有一个所有者
- 3.当所有者(变量)离开作用域范围时,这个值将被丢弃(drop)
- 4,所有权的转移
move
语句,单独拥有所有权 - 5.深拷贝,关键词–
clone()
- 6.COPY的特征:任何基本类型的组合可以
Copy
, 不需要分配内存或某种形式资源的类型是可以Copy
的。- 所有整型类型,比如
u32
- 布尔类型,bool, 它的值是
true
和false
- 所有浮点数类型,比如
f64
- 字符类型,
char
- 元组, 当且仅当其包含的类型也都是
Copy
的时候。比如(i32,i32)
是copy
的,但(i32,String)
就不是 - 不可变引用
&T
,例如&str
, 但是注意:可变引用&mut T
是不可以Copy
的。
- 所有整型类型,比如
- 7.同一时刻,你只能拥有要么一个可变引用,要么任意多个不可变引用
- 引用必须总是有效的。
4.结构体
- 1.初始化结构体实例时,每个字段都需要进行初始化。
- 2.初始化时的字段顺序不需要和结构体定义时的顺序一致
- 3.结构体的所有权会出现一部分出现转移,一部分可以使用
5.枚举
- 1.枚举成员可以持有各种类型的值
- 2.通过
Option<T>
枚举来处理为空的情况,Rust
中没有null。
6.数组
- 1.长度固定
- 2.元素必须有相同的类型
- 3.依次线性排列
let a: [i32; 5] = [1,2,3,4,5] ; let a = [3; 5]
- 4.数组元素为非基础类型
7.流程控制
-
for i in 1..=5 {}
使用for我们往往使用集合的引用形式,for item in &container {}
for item in collection for item in IntoIterator::into_iter(collection)
for item in &collection for item in collection.iter()
for item in &mut collection for item in collection.iter_mut()
-
break
可以单独使用,也可以带一个返回值,有些类似return。
8.模式使用场景
- 1.
match
匹配是穷尽式的,if let
往往用于匹配一个模式,而忽略剩下的所有模式的场景。 -
while let
类似的结构是while let
条件循环,他允许只要模式匹配就一直进行while
循环,while let Some(top) = stack.pop(){}
-
- 模式匹配一定要类型相同。
-
- 使用下划线开头忽略未使用的变量
_x
- 使用下划线开头忽略未使用的变量
9.泛型
struct Point<T, U> {
x: T,
y: U,
}
impl<T, U> Point<T, U> {
// 实现 mixup,不要修改其它代码!
fn mixup<V,W>(self,other:Point<V,W>) -> Point<T,W> {
Point {
x: self.x,
y: other.y,
}
}
}
fn main() {
let p1 = Point { x: 5, y: 10 };
let p2 = Point { x: "Hello", y: '中'};
let p3 = p1.mixup(p2);
assert_eq!(p3.x, 5);
assert_eq!(p3.y, '中');
}
// 修复错误,让代码工作
struct Point<T> {
x: T,
y: T,
}
impl Point<f32> {
fn distance_from_origin(&self) -> f32 {
(self.x.powi(2) + self.y.powi(2)).sqrt()
}
}
fn main() {
let p = Point{x: 5_f32, y: 10_f32}; // 指定f32的类型
println!("{}",p.distance_from_origin())
}
10.特征trait
-
- 特征定义与实现的位置(孤儿规则)
- 如果你想为A实现特征T,那么A或者T至少有一个在当前作用域中定义的。
- 2.使用特征作为函数参数
-
- 使用
Derive
派生
- 使用
-
- 特征约束
fn main() { assert_eq!(sum(1, 2), 3); } // 通过两种方法使用特征约束来实现 `fn sum` //fn sum<T: std::ops::Add<T, Output=T>>(x: T, y: T) -> T { // x + y //} fn sum<T>(x:T, y:T) -> T where T:std::ops::Add<T, Output=T>, { x + y }
fn some_function<T: Display + Clone, U: Clone + Debug>(t: &T, u: &U) -> i32 {}
fn some_function<T, U>(t: &T, u: &U) -> i32 where T: Display + Clone,U: Clone + Debu
11.特征对象
-
- 鸭子类型-duck typing, 一个东西走起来像鸭子,同时叫起来像鸭子,就是一只鸭子。
- 2.特征对象的动态分发
- 两个指针组成,一个
ptr
和vptr
。 - 两个
self
,self
指代当前的实例对象,Self
指代特征或者方法类型的别名
- 两个指针组成,一个
- 3.只有对象安全的特征才拥有特征对象
- 方法的返回类型不能是
Self
,特种对象有可能忘记了其真正的类型,返回Self
就不认识了 - 方法没有任何泛型参数。因为使用泛型类型参数来说,当使用特征时其会放入具体的类型参数。特征对象其具体类型被抹去了。
- 方法的返回类型不能是
- 4.对象安全
// 使用至少两种方法让代码工作
// 不要添加/删除任何代码行
trait MyTrait {
fn f(&self) -> Self;
}
impl MyTrait for u32 {
fn f(&self) -> Self { 42 }
}
impl MyTrait for String {
fn f(&self) -> Self { self.clone() }
}
fn my_function(x: Box<dyn MyTrait>) {
x.f()
}
fn main() {
my_function(Box::new(13_u32));
my_function(Box::new(String::from("abc")));
println!("Success!")
}
// one
trait MyTrait {
fn f(&self) -> Self;
}
impl MyTrait for u32 {
fn f(&self) -> u32 { 42 }
}
impl MyTrait for String {
fn f(&self) -> String { self.clone() }
}
fn my_function(x: impl MyTrait) -> impl MyTrait {
x.f()
}
fn main() {
my_function(13_u32);
my_function(String::from("abc"));
}
//two
trait MyTrait {
fn f(&self) -> Box<dyn MyTrait>;
}
impl MyTrait for u32 {
fn f(&self) -> Box<dyn MyTrait> { Box::new(42) }
}
impl MyTrait for String {
fn f(&self) -> Box<dyn MyTrait> { Box::new(self.clone()) }
}
fn my_function(x: Box<dyn MyTrait>) -> Box<dyn MyTrait> {
x.f()
}
fn main() {
my_function(Box::new(13_u32));
my_function(Box::new(String::from("abc")));
}
12.深入了解特征
-
- 默认泛型类型参数
struct Container(i32, i32);
// 使用关联类型实现重新实现以下特征
// trait Contains {
// type A;
// type B;
//trait Contains<A, B> {
trait Contains {
type A;
type B;
//fn contains(&self, _: &A, _: &B) -> bool;
fn contains(&self, _: &Self::A, _: &Self::B) -> bool;
fn first(&self) -> i32;
fn last(&self) -> i32;
}
//impl Contains<i32, i32> for Container {
impl Contains for Container {
type A = i32;
type B = i32;
fn contains(&self, number_1: &i32, number_2: &i32) -> bool {
(&self.0 == number_1) && (&self.1 == number_2)
}
// Grab the first number.
fn first(&self) -> i32 { self.0 }
// Grab the last number.
fn last(&self) -> i32 { self.1 }
}
//fn difference<A, B, C: Contains<A, B>>(container: &C) -> i32 {
fn difference<C: Contains>(container: &C) -> i32 {
container.last() - container.first()
}
fn main() {
let number_1 = 3;
let number_2 = 10;
let container = Container(number_1, number_2);
println!("Does container contain {} and {}: {}",
&number_1, &number_2,
container.contains(&number_1, &number_2));
println!("First number: {}", container.first());
println!("Last number: {}", container.last());
println!("The difference is: {}", difference(&container));
}
use std::ops::Sub;
#[derive(Debug, PartialEq)]
struct Point<T> {
x: T,
y: T,
}
// 用三种方法填空: 其中两种使用默认的泛型参数,另外一种不使用
//impl<T:Sub<Output=T>> Sub<Self> for Point<T> {
//impl<T:Sub<Output=T>> Sub for Point<T> {
impl<T:Sub<Output=T>> Sub<Point<T>> for Point<T> {
type Output = Self;
fn sub(self, other: Self) -> Self::Output {
Point {
x: self.x - other.x,
y: self.y - other.y,
}
}
}
fn main() {
assert_eq!(Point { x: 2, y: 3 } - Point { x: 1, y: 0 },
Point { x: 1, y: 3 });
println!("Success!")
}
- 2.完全限定语法
println!("A baby dog is called a {}", <Dog as Animal>::baby_name());
- 3.特征定义中的特征约束
- 某个特征A能使用另一个特征B的功能,实现不仅仅为类型实现特征A,还有为类型实现特征B才行。
trait Person {
fn name(&self) -> String;
}
// Person 是 Student 的 supertrait .
// 实现 Student 需要同时实现 Person.
trait Student: Person {
fn university(&self) -> String;
}
trait Programmer {
fn fav_language(&self) -> String;
}
// CompSciStudent (computer science student) 是 Programmer
// 和 Student 的 subtrait. 实现 CompSciStudent 需要先实现这两个 supertraits.
trait CompSciStudent: Programmer + Student {
fn git_username(&self) -> String;
}
fn comp_sci_student_greeting(student: &dyn CompSciStudent) -> String {
format!(
"My name is {} and I attend {}. My favorite language is {}. My Git username is {}",
student.name(),
student.university(),
student.fav_language(),
student.git_username()
)
}
struct CSStudent {
name: String,
university: String,
fav_language: String,
git_username: String
}
// 为 CSStudent 实现所需的特征
impl Person for CSStudent {
fn name(&self) ->String {
self.name.clone()
}
}
impl Student for CSStudent {
fn university(&self) -> String {
self.university.clone()
}
}
impl Programmer for CSStudent {
fn fav_language(&self) -> String {
self.fav_language.clone()
}
}
impl CompSciStudent for CSStudent {
fn git_username(&self) -> String{
self.git_username.clone()
}
}
fn main() {
let student = CSStudent {
name: "Sunfei".to_string(),
university: "XXX".to_string(),
fav_language: "Rust".to_string(),
git_username: "sunface".to_string()
};
// 填空
println!("{}", comp_sci_student_greeting(&student));
}
- 4.在外部类型上实现外部特征
- 绕过孤儿规则(特征或者类型必须至少有一个是本地的,才能在此类型上定义特征),重新
newtype
元组结构体struct Wrapper(Vec<String>)
- 绕过孤儿规则(特征或者类型必须至少有一个是本地的,才能在此类型上定义特征),重新
use std::fmt;
// 定义一个 newtype `Pretty`
struct Pretty(String);
impl fmt::Display for Pretty {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "\"{}\"", self.0.clone() + ", world")
}
}
fn main() {
let w = Pretty("hello".to_string());
println!("w = {}", w);
}
13.动态数组Vector
- 1.vector中仅允许存储相同类型的数据,一般以特种,或者相同的类型。
-
- 基本函数
let v = vec![1, 2, 3];
// array -> Vec
let arr = [1, 2, 3];
let v1 = Vec::from(arr);
let v2: Vec<i32> = arr.into();
// String -> Vec
let s = "hello".to_string();
let v1: Vec<u8> = s.into();
let s = "hello".to_string();
let v2 = s.into_bytes();
assert_eq!(v1, v2);
let s = "hello";
let v3 = Vec::from(s);
assert_eq!(v2, v3);
14.生命周期消除
- 1.每一个引用参数都会获得独自的生命周期
- 例如一个引用参数的函数就有一个生命周期标注:
fn foo<'a>(x: &'a i32)
,两个引用参数的有两个生命周期标注:fn foo<'a, 'b>(x: &'a i32, y: &'b i32)
,依次类推
- 例如一个引用参数的函数就有一个生命周期标注:
- 2.若只有一个输入生命周期(函数参数中只有一个引用标注), 那么该声明周期会被赋给所有的输出生命周期,也就是所有返回值的生命周期都等于该输入生命周期。
- 例如函数
fn foo(x: &i32) -> &i32
,x
参数的生命周期会被自动赋给返回值&i32
,因此该函数等同于fn foo<'a>(x: &'a i32) -> &'a i32
- 例如函数
- 3.若存在多个生命周期,且其中一个是
&self
或&mut self
, 则&self
的生命周期被赋给所有的的输出生命周期- 拥有
&self
形式的参数,说明该函数是一个 方法,该规则让方法的使用便利度大幅提升。
- 拥有
14.1 无界生命周期,
-
- 尽量避免这种无界生命周期。若一个输出生命周期被消除了,那么必定因为有一个输入生命周期与之对应。
-
- 生命周期约束 HRTB
'a:'b
表示'a
至少活得跟'b
一样久。
- 生命周期约束 HRTB
- 3.闭包函数的消除规则
用Fn特征解决闭包生命周期
fn main() { let closure_slision = fun(|x: &i32| -> &i32 { x }); assert_eq!(*closure_slision(&45), 45); // Passed ! } fn fun<T, F: Fn(&T) -> &T>(f: F) -> F { f }
-
Reborrow
再借用,再借用
以后,在它的生命周期里不能使用借用
.
fn main() {
let mut p = Point { x: 0, y: 0 };
let r = &mut p;
// reborrow! 此时对`r`的再借用不会导致跟上面的借用冲突
let rr: &Point = &*r;
//r.move_to(10, 10); 出错
// 再借用`rr`最后一次使用发生在这里,在它的生命周期中,我们并没有使用原来的借用`r`,因此不会报错
println!("{:?}", rr);
// 再借用结束后,才去使用原来的借用`r`
r.move_to(10, 10);
println!("{:?}", r);
}
一个复杂的示例:只能有一个可变调用
struct Interface<'b, 'a: 'b> {
manager: &'b mut Manager<'a>
}
impl<'b, 'a: 'b> Interface<'b, 'a> {
pub fn noop(self) {
println!("interface consumed");
}
}
struct Manager<'a> {
text: &'a str
}
struct List<'a> {
manager: Manager<'a>,
}
impl<'a> List<'a> {
pub fn get_interface<'b>(&'b mut self) -> Interface<'b, 'a>
where 'a: 'b {
Interface {
manager: &mut self.manager
}
}
}
fn main() {
let mut list = List {
manager: Manager {
text: "hello"
}
};
list.get_interface().noop();
println!("Interface should be dropped here and the borrow released");
// 下面的调用可以通过,因为Interface的生命周期不需要跟list一样长
use_list(&list);
}
fn use_list(list: &List) {
println!("{}", list.manager.text);
}
15.panic深入剖析
-
- backtrace 栈展开
- 那就按照提示使用
RUST_BACKTRACE=1 cargo run
或$env:RUST_BACKTRACE=1
;cargo run
来再一次运行程序:
16.使用use引入模块及受限可见性。
- 1.限制可见性语法
pub
意味着可见性无任何限制pub(crate)
表示在当前包可见pub(self)
在当前模块可见pub(super)
在父模块可见pub(in <path>)
表示在某个路径表的模块中可见,其中path
必须是父模块或者祖先模块。