Rust 基础入门-Summary

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, 它的值是 truefalse
    • 所有浮点数类型,比如 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.流程控制

    1. 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()
    1. break 可以单独使用,也可以带一个返回值,有些类似return。

8.模式使用场景

  • 1.match匹配是穷尽式的,if let 往往用于匹配一个模式,而忽略剩下的所有模式的场景。
    1. while let类似的结构是 while let条件循环,他允许只要模式匹配就一直进行 while循环, while let Some(top) = stack.pop(){}
    1. 模式匹配一定要类型相同。
    1. 使用下划线开头忽略未使用的变量 _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

    1. 特征定义与实现的位置(孤儿规则)
    • 如果你想为A实现特征T,那么A或者T至少有一个在当前作用域中定义的。
  • 2.使用特征作为函数参数
    1. 使用 Derive派生
    1. 特征约束
    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.特征对象

    1. 鸭子类型-duck typing, 一个东西走起来像鸭子,同时叫起来像鸭子,就是一只鸭子。
  • 2.特征对象的动态分发
    • 两个指针组成,一个 ptrvptr
    • 两个 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.深入了解特征

    1. 默认泛型类型参数
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中仅允许存储相同类型的数据,一般以特种,或者相同的类型。
    1. 基本函数
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) -> &i32x 参数的生命周期会被自动赋给返回值 &i32,因此该函数等同于 fn foo<'a>(x: &'a i32) -> &'a i32
  • 3.若存在多个生命周期,且其中一个是 &self&mut self, 则 &self 的生命周期被赋给所有的的输出生命周期
    • 拥有 &self 形式的参数,说明该函数是一个 方法,该规则让方法的使用便利度大幅提升。

14.1 无界生命周期,

    1. 尽量避免这种无界生命周期。若一个输出生命周期被消除了,那么必定因为有一个输入生命周期与之对应。
    1. 生命周期约束 HRTB 'a:'b 表示 'a 至少活得跟 'b 一样久。
  • 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
    }
    
    1. 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深入剖析

    1. 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 必须是父模块或者祖先模块。
  • 10
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值