Rust学习笔记

mac 安装rust(rustc为编译器)
1. brew install rustup2. cd /usr/local/Cellar/rustup-init/1.16.0/bin3. ./rustup-init4. vim ~/.bashrc5. export PATH=$HOME/.cargo/bin:$PATH(4、5可以用一步替换 echo 'export PATH=$HOME/.cargo/bin:$PATH' | sudo  tee -a ~/.bashrc)6. source ~/.bashrc
Cargo 基本命令
1. cargo new 项目名 --bin //如果项目名字已经创建好,切到该目录下执行cargo init2. cargo build  // 编译项目 --release 发布优化3. cargo run // 编译并运行
配置国内依赖源镜像:
[source.crates-io]registry = "https://github.com/rust-lang/crates.io-index"replace-with = 'ustc'[source.ustc]registry = "git://mirrors.ustc.edu.cn/crates.io-index"
所有权系统
1. rust中的每一个值都有且只有一个称为所有者的变量与之绑定。2. 当所有者离开作用域时,这个值将被丢弃。(与值类型相关)
  • 变量与数据的交互方式

    • 移动(引用类型):类似于浅拷贝(共用底层数据),为避免释放内存时会造成二次释放,使失去值所有权的变量失效,该过程成为

      所有权转移

    • 克隆 (实现引用类型的深拷贝),没有所有权转移

    • 只在栈上的数据(值类型),没有所有权转移一说

变量的所有权总是遵循相同的模式:将值赋给另一个变量时移动它。当持有堆中数据的变量离开作用域时,其值将通过drop被清理掉,除非数据被移动为其他变量所有。

fn main() {   
    let s : String = String::from("hello");  // s进入作用域
    take_ownership(s); // s  将值"hello"的所有权转移给了函数take_ownership()
    println!("{}", s); // s 已失效
    let i = 10;  // i 进入作用域
    take_ownership1(i); // 
    println!("{}", i); // i依然有效。}fn take_ownership(s : String) {    println!("s is {}",s);
}fn take_ownership1(i : i8) {    println!("{}", i);
}
  • 所有权借用、引用(默认不可变,与指针类似但不同(rust 中在编译阶段就能发现空指针))(&、*)

    • 不可变引用 可以有多个但

      不能和可变引用共存
    • 可变引用,任意给定时间内只能有一个

fn main() {let s1 = String::from("hello");    let len = calculate_length(&s1);    println!("The length of '{}' is {}.", s1, len);
}fn calculate_length(s: &String) -> usize {
    s.len()
}
  • slice

1. 没有所有权的数据类型2. 字符串文本是&str类型
    let string = String::from("hello world");    let s2 = &string[0..3]; // 不包括3
    let s3 = &string[0..=3]; // 包括3
结构体方法、关联函数
  • 结构体不允许只将某个字段设置为可变,要么整体可变,要么整体不可变,结构体应该持有字段的所有权不然的话,需要指明额外控制(生命周期)。

#[derive(Debug)]struct User {
    user_name : String,
    age :   u8}impl User {    fn user_name(&self) -> &str { // user_name(&self)称为方法
        &self.user_name
    }    fn new(name : &str, age :u8) -> User { // 称为关联方法(调用符号::)
        User {
            user_name : String::from(name),
            age
        }
    }
    
}fn main() {    let user = User::new("liyan", 27);    println!("{:#?},{}",user,user.user_name());
}
------// tipsUser {
        email, // 当变量名和字段名相同时,可简写
        username,
        active: true,
        sign_in_count: 1,
}let user2 = User {
    email: String::from("another@example.com"),
    username: String::from("anotherusername567"),
    ..user1  // 字段值引用};
枚举和模式匹配(match)
enum IpAddrKind1 {
    V4(String),
    V6(String)
}
---------IpAddrKind1 = IPAddr + IpAddrKind ----------struct IPAddr {
    kind : IpAddrKind,
    address : String,
}enum IpAddrKind {
    V4,
    V6
}
---------- 标准库中枚举的使用 ----------pub enum Option<T> {    /// No value
    #[stable(feature = "rust1", since = "1.0.0")]
    None,    /// Some value `T`
    #[stable(feature = "rust1", since = "1.0.0")]
    Some(#[stable(feature = "rust1", since = "1.0.0")] T),
}pub enum Result<T, E> {    /// Contains the success value
    #[stable(feature = "rust1", since = "1.0.0")]
    Ok(#[stable(feature = "rust1", since = "1.0.0")] T),    /// Contains the error value
    #[stable(feature = "rust1", since = "1.0.0")]
    Err(#[stable(feature = "rust1", since = "1.0.0")] E),
}
-----match 模式匹配fn main() {   println!("Guess the number!");    let secret_number= rand::thread_rng().gen_range(1, 101);    loop {        println!("please input your guess!");        let mut guess = String::new();
        io::stdin().read_line(&mut guess)
            .expect("failed to read line");        let guess : u32 = match guess.trim().parse() {            Ok(num) => num, // Result<OK,Err> 枚举,绑定值模式
            Err(_) => continue
        };        println!("your guess number is {}", guess);        match guess.cmp(&secret_number) { // match 对枚举进行模式匹配
            Ordering::Less => println!("Too small!"),
            Ordering::Greater => println!("Too big"),
            Ordering::Equal => {                println!("You win");                break;
            }
        }
    }
}
------- if let 单一模式匹配 ----let mut count = 0;match coin {
    Coin::Quarter(state) => println!("State quarter from {:?}!", state),
    _ => count += 1,
}
---- let mut count = 0;if let Coin::Quarter(state) = coin {    println!("State quarter from {:?}!", state);
} else {
count += 1; }
模块系统
  • extren crate

  • 导入 use crate as ..

  • pub 可见性

  • mod 模块

  • super super 开头的相对路径 ../

  • pub use 重导出

  • 嵌套路径

mod sound{    mod instrument {        mod woodwind {            fn clarinet() {
                
            } 
            
        }
}mod voice { }
}fn main() { }
---mod sound{    pub mod instrument {        pub fn clarinet() {            //} }
}mod performance_group {    pub use crate::sound::instrument;    pub fn clarinet_trio() {
        instrument::clarinet();
        instrument::clarinet();
        instrument::clarinet();
} }fn main() {
    performance_group::clarinet_trio();
    performance_group::instrument::clarinet();
}

----------use std::cmp::Ordering;use std::io;
--- 两者等价(嵌套路径)use std::{cmp::Ordering, io};
通用集合类型
  • vector 可变值,只能存储相同类型的值

  • String 可变值

  • map

// vec push()、pop()
 let mut v: Vec<u8> = Vec::new();
  v.push(5); let v1 : Vec<u8> = vec![1,2,3]; for i in &v {  // 遍历
        println!("{}",i)
    }    for i in &mut v {
        *i += 50;  // 解引用
    }// String // mapHashmaplet mut map: HashMap<String, String> = HashMap::new();
 map.insert(String::from("liyan"), String::from("sweepingMonk"));

 map.entry(String::from("lyan")).or_insert(String::from("goodman")); let value = match  map.get(&String::from("lyan")) {     Some(v) => v,     None => "" ,
 }; println!("{:?}",value)
错误处理(可恢复错误(Result<T,E>枚举)、不可恢复错误(panic!()宏))
panic时的栈处理(展开(默认)、终止(由操作系统做收尾工作))Cargo.toml中添加:[profile.release] 
 panic = 'abort'
RUST_BACKTRACE 环境变量设置为非零值,在非release  环境下会打印堆栈信息(在release下需要开启debug标识,非release下debug默认开启)匹配错误类型let f = match  File::open("hello.txt") {    Ok(file) => file,    Err(error) => match error.kind() {
        ErrorKind::NotFound => match File::create("hello.txt") {            Ok(fc) => fc,            Err(e) => panic!("Tried to create file but there was a problem: {:?}",
                             e),
        },
        other_error => panic!("There was a problem opening the file: {:?}",
                              other_error),
    }, 
};
-------------- let f = File::open("hello.txt").map_err(|error| {        if error.kind() == ErrorKind::NotFound {
            File::create("hello.txt").unwrap_or_else(|error| {                panic!("Tried to create file but there was a problem: {:?}", error);
            })

        } else {            panic!("There was a problem opening the file: {:?}", error);

} });

unwarp 和expect    let f = File::open("hello.txt").unwrap();  // 如果Result的成员是OK,返回OK中的值,如果是成员Err,则调用panic!
 let f = File::open("hello.txt").expect("Failed to open hello.txt"); //  如果失败抛出错误错误传播fn read_username_from_file() -> Result<String, io::Error> {    let f = File::open("hello.txt");    let mut f = match f {        Ok(file) => file,        Err(e) => return Err(e),
    };    let mut s = String::new();    match f.read_to_string(&mut s) {        Ok(_) => Ok(s),        Err(e) => Err(e),
    }
}
----传播错误简写 <?>只能用于返回Result的函数fn read_username_from_file() -> Result<String, io::Error> {    let mut f = File::open("hello.txt")?;    let mut s = String::new();
    f.read_to_string(&mut s)?;Ok(s) 
}
-----fn read_username_from_file3() -> Result<String, io::Error> {    let mut s = String::new();
    File::open("hello.txt")?.read_to_string(&mut s)?;    Ok(s)
}
-------与上面等价的写法fn read_username_from_file() -> Result<String, io::Error> {
    fs::read_to_string("hello.txt")

}
泛型、接口(trait)和生命周期(允许我们向编译器提供引用如何相互关联的泛型)

原理: rust通过编译时进行泛型代码的单态化保证效率,单态化是指一个通过填充编译时使用的具体类型,将代码转换为特定代码的过程。

有效引用的特性:数据有着比引用更长的生命周期

// 泛型函数fn largest<T>(list: &[T]) -> T { 
    let mut largest = list[0];    for &item in list.iter() {        if item > largest {
            largest = item;
        } 
    }
    largest
}
---// 泛型结构体struct Point<T> {
    x: T,

y: T, }struct Point<T, U> {
    x: T,

y: U, }
---// 泛型枚举enum Option<T> {    Some(T),None, }enum Result<T, E> {    Ok(T),Err(E), }
---------// 泛型方法struct Point<T> {
    x: T,

y: T,
}impl<T> Point<T> {    fn x(&self) -> &T {
      &self.x 
}impl Point<u8> { 
}
}struct Point<T, U> {
    x: T,
    y: U, 
    
}impl<T, U> Point<T, U> {    fn mixup<V, W>(self, other: Point<V, W>) -> Point<T, W> {

        Point {
            x: self.x,
            y: other.y, 
        }
    } 
}
------// trait 作为参数
 pub fn notify(item: impl Summary) { 
    println!("Breaking news! {}", item.summarize());
}// 泛型约束pub fn notify<T: Summary>(item1: T, item2: T);pub fn notify(item1: impl Summary, item2: impl Summary);pub fn notify(item: impl Summary + Display);pub fn notify<T: Summary + Display>(item: T);// where 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 + Debug;
-------// trait 作为返回值,只能返回单一类型fn returns_summarizable() -> impl Summary {
    Tweet { 
        username: String::from("horse_ebooks"),
        content: String::from("of course, as you probably already know, people"),
        reply: false,
        retweet: false,
    } 
}// 编译错误,返回的具体类型不确定fn returns_summarizable(switch: bool) -> impl Summary { 
    if switch {
        NewsArticle { 
            headline: String::from("Penguins win the Stanley Cup Championship!"),
            location: String::from("Pittsburgh, PA, USA"),
            author: String::from("Iceburgh"),
            content: String::from("The Pittsburgh Penguins once again are the best
            hockey team in the NHL."),
        }
    } else {
        Tweet { 
            username: String::from("horse_ebooks"),
            content: String::from("of course, as you probably already know, people"),
            reply: false,
            retweet: false,
        } 
    }
}
-----//  使用泛型约束有条件的实现方法(blanket implementations)use std::fmt::Display;struct Pair<T> {
    x: T,
    y: T, 
}// 为所有T类型结构体实现方法new()impl<T> Pair<T> {    fn new(x: T, y: T) -> Self {        Self { 
            x, 
            y,
        } 
    }
}// 只为实现了Display 和 PartialOrd trait的T类型结构体实现cmp_disolay()方法impl<T: Display + PartialOrd> Pair<T> {    fn cmp_display(&self) {        if self.x >= self.y {            println!("The largest member is x = {}", self.x);
        } else {            println!("The largest member is y = {}", self.y);
        } 
    }
}// 为所有实现了Display的T类型实现to_string方法impl<T: Display> ToString for T 
--------------------------------------------// 泛型生命周期参数 'a// 借用检查器工作方式---编译错误{    let r;                // ---------+-- 'a
                          //          |
    {                     //          |
        let x = 5;        // -+-- 'b  |
        r = &x;           //  |       |
    }                     // -+       |
                          //          |
    println!("r: {}", r); //  r尝试引用的x离开作用域,造成空指针。        |}                         // ---------+------OK#![allow(unused_variables)]fn main() {
 {    let x = 5;                // ----------+-- 'b
                              //           |
    let r = &x;               // --+-- 'a  |
                              //   |       |
    println!("r: {}", r);     //   |       |
                              // --+       |
 }                            // ----------+}
----// 函数中的泛型生命周期fn main() {    let string1 = String::from("abcd");    let string2 = "xyz";    let result = longest(string1.as_str(), string2);    println!("The longest string is {}", result);
}
---编译错误 fn longest(x: &str, y: &str) -> &str {    if x.len() > y.len() {
        x
    } else {
        y
    }
}// error[E0106]: missing lifetime specifier//  --> src/main.rs:1:33//   |// 1 | fn longest(x: &str, y: &str) -> &str {//   |                                 ^ expected lifetime parameter//   |//   = help: this function's return type contains a borrowed value, but the// signature does not say whether it is borrowed from `x` or `y`----生命周期注解语法: 生命周期参数名称必须以撇号(')开头,其名称通常全是小写,类似于泛型其名称非常短。'a 是大多数人默认使用的名称。生命周期参数注解位于引用的 & 之后,并有一个空格来将引用类型与生命周期注解分隔开&i32        // 引用&'a i32     // 带有显式生命周期的引用&'a mut i32 // 带有显式生命周期的可变引用----// 函数签名中的生命周期注解(只出现在函数签名中,而不存在于函数体中,表达的是一种关联关系)1. 在函数签名中指定生命周期参数时,我们并没有改变任何传入后返回的值的生命周期。而是指出任何不遵守这个协议的传入值都将被借用检查器拒绝.2. 当从函数返回一个引用,返回值的生命周期参数需要与一个参数的生命周期参数相匹配。如果返回的引用 没有 指向任何一个参数,那么唯一的可能就是它指向一个函数内部创建的值,它将会是一个悬垂引用,因为它将会在函数结束时离开作用域3. 生命周期语法是用于将函数的多个参数与其返回值的生命周期进行关联的。一旦他们形成了某种关联,Rust 就有了足够的信息来允许内存安全的操作并阻止会产生悬垂指针亦或是违反内存安全的行为.fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {    if x.len() > y.len() {
        x
    } else {
        y
    }
}
------// 结构体中的生命周期struct ImportantExcerpt<'a> {
    part: &'a str,
}fn main() {    let novel = String::from("Call me Ishmael. Some years ago...");    let first_sentence = novel.split('.')
        .next()
        .expect("Could not find a '.'");    let i = ImportantExcerpt { part: first_sentence };
}
----// 生命周期省略规则(适用于 fn(函数) 定义,以及 impl(方法) 块)fn first_word(s: &str) -> &str { // 生命周期缺省
    let bytes = s.as_bytes();    for (i, &item) in bytes.iter().enumerate() {        if item == b' ' {            return &s[0..i];
        }
    }

    &s[..]
}fn first_word<'a>(s: &'a str) -> &'a str----// 函数或方法的参数的生命周期被称为 输入生命周期(input lifetimes),而返回值的生命周期被称为 输出生命周期(output lifetimes)。1. 每一个是引用的参数都有它自己的生命周期参数。换句话说就是,有一个引用参数的函数有一个生命周期参数:fn foo<'a>(x: &'a i32),有两个引用参数的函数有两个不同的生命周期参数2. 如果只有一个输入生命周期参数,那么它被赋予所有输出生命周期参数3. 如果方法有多个输入生命周期参数,不过其中之一因为方法的缘故为 &self 或 &mut self,那么 self 的生命周期被赋给所有输出生命周期参数// 方法定义中的生命周期struct ImportantExcerpt<'a> {
    part: &'a str,
}impl<'a> ImportantExcerpt<'a> {    fn announce_and_return_part(&self, announcement: &str) -> &str {        println!("Attention please: {}", announcement);        self.part
    }
}// 静态生命周期1. 'static,其生命周期存活于整个程序期间。所有的字符串字面值都拥有 'static 生命周期(字符串的文本被直接储存在程序的二进制文件中而这个文件总是可用的)

借用检查器: Rust编译器的组件,用来比较作用域确保所有的借用都是有效的。

迭代器与闭包

闭包 :可以保存进变量或作为参数传递给其他函数的匿名函数(可以捕获环境的匿名函数)

  • 闭包可以通过三种方式捕获其环境,他们直接对应函数的三种获取参数的方式:获取所有权,可变借用和不可变借用。这三种捕获值的方式被编码为如下三个 Fn trait:

    • FnOnce 消费从周围作用域捕获的变量,闭包周围的作用域被称为其 环境,environment。为了消费捕获到的变量,闭包必须获取其所有权并在定义闭包时将其移动进闭包。其名称的 Once 部分代表了闭包不能多次获取相同变量的所有权的事实,所以它只能被调用一次。

      move能实现相同的功能
    • FnMut 获取可变的借用值所以可以改变其环境

    • Fn 从其环境获取不可变的借用值

#![allow(unused_variables)]fn main() {use std::thread;use std::time::Duration;let expensive_closure = |num| { // 闭包语法
    println!("calculating slowly...");
    thread::sleep(Duration::from_secs(2));
    num
};
expensive_closure(5);
}// 闭包类型 所有的闭包都实现了 trait Fn、FnMut 或 FnOnce 中的一个(类似于go中的函数类型)#![allow(unused_variables)]fn main() {struct Cacher<T>    where T: Fn(u32) -> u32{
    calculation: T,
    value: Option<u32>,
}impl<T> Cacher<T>    where T: Fn(u32) -> u32{    fn new(calculation: T) -> Cacher<T> {
        Cacher {
            calculation,
            value: None,
        }
    }    fn value(&mut self, arg: u32) -> u32 {        match self.value {            Some(v) => v,            None => {                let v = (self.calculation)(arg);                self.value = Some(v);
                v
            },
        }
    }
}
}
----fn main() {    let x = 4;    let equal_to_x = |z| z == x;  // 捕获环境变量

    let y = 4;    assert!(equal_to_x(y));
}fn main() { // 编译错误
    let x = vec![1, 2, 3];    let equal_to_x = move |z| z == x; // x 被移动进了闭包,因为闭包使用 move 关键字定义。接着闭包获取了 x 的所有权,同时 main 就不再允许在 println! 语句中使用 x 了。去掉 println! 即可修复问题。

    println!("can't use x here: {:?}", x);    let y = vec![1, 2, 3];    assert!(equal_to_x(y));
}// 迭代器iter 方法生成一个不可变引用的迭代器。 into_iter 方法生成一个拥有所有权的迭代器。iter_mut 方法生成一个可变引用的迭代器。let v1 = vec![1, 2, 3];let v1_iter = v1.iter();for val in v1_iter {    println!("Got: {}", val);
}trait Iterator { //  type Item 和 Self::Item 为关联类型
    type Item;    fn next(&mut self) -> Option<Self::Item>; 

    // 此处省略了方法的默认实现}#[test]fn iterator_demonstration() {    let v1 = vec![1, 2, 3];    let mut v1_iter = v1.iter();    assert_eq!(v1_iter.next(), Some(&1));    assert_eq!(v1_iter.next(), Some(&2));    assert_eq!(v1_iter.next(), Some(&3));    assert_eq!(v1_iter.next(), None);    // v1_iter 需要是可变的:在迭代器上调用 next 方法改变了迭代器中用来记录序列位置的状态。换句话说,代码 消费(consume)了,或使用了迭代器。每一个 next 调用都会从迭代器中消费一个项。
    // 使用 for 循环时无需使 v1_iter 可变因为 for 循环会获取 v1_iter 的所有权并在后台使 v1_iter 可变}// 消费适配器(消费迭代器的方法)// sum(),collect(),#[test]fn iterator_sum() {    let v1 = vec![1, 2, 3];    let v1_iter = v1.iter();    let total: i32 = v1_iter.sum(); // sum消费了迭代器

    assert_eq!(total, 6);
}// 消费器适配器(产生迭代器的方法)// map(),filter(),zip(),let v1: Vec<i32> = vec![1, 2, 3];let v2: Vec<_> = v1.iter().map(|x| x + 1).collect();assert_eq!(v2, vec![2, 3, 4]);#![allow(unused_variables)]fn main() {#[derive(PartialEq, Debug)]struct Shoe {
    size: u32,
    style: String,
}fn shoes_in_my_size(shoes: Vec<Shoe>, shoe_size: u32) -> Vec<Shoe> {
    shoes.into_iter()   //  into_iter 来创建一个获取 vector 所有权的迭代器
        .filter(|s| s.size == shoe_size)
        .collect()
}#[test]fn filters_by_size() {    let shoes = vec![
        Shoe { size: 10, style: String::from("sneaker") },
        Shoe { size: 13, style: String::from("sandal") },
        Shoe { size: 10, style: String::from("boot") },
    ];    let in_my_size = shoes_in_my_size(shoes, 10);    assert_eq!(
        in_my_size,        vec![
            Shoe { size: 10, style: String::from("sneaker") },
            Shoe { size: 10, style: String::from("boot") },
        ]
    );
}
}// 自定义迭代器(实现Iterator trait)struct Counter {
    count: u32,
}impl Counter {    fn new() -> Counter {
        Counter { count: 0 }
    }
}impl Iterator for Counter {    type Item = u32;    fn next(&mut self) -> Option<Self::Item> {        self.count += 1;        if self.count < 6 {            Some(self.count)
        } else {            None
        }
    }
}
智能指针
Box<T> 类型是一个智能指针,因为它实现了 Deref trait,它允许 Box<T> 值被当作引用对待。当 Box<T> 值离开作用域时,由于 Box<T> 类型 Drop trait 的实现,box 
// 实现 Deref trait 允许我们重载 解引用运算符(dereference operator)*(与乘法运算符或 glob 运算符相区别)。通过这种方式实现 Deref trait 的智能指针可以被当作常规引用来对待,可以编写操作引用的代码并用于智能指针// 解引用强制多态(deref coercions)是 Rust 表现在函数或方法传参上的一种便利。其将实现了 Deref 的类型的引用转换为原始类型通过 Deref 所能够转换的类型的引用use std::ops::Deref;

struct MyBox<T>(T);

impl<T> MyBox<T> {
    fn new(x: T) -> MyBox<T> {
        MyBox(x)
    }
}

impl<T> Deref for MyBox<T> {
    type Target = T;

    fn deref(&self) -> &T {        &self.0
    }
}

fn hello(name: &str) {
    println!("Hello, {}!", name);
}

fn main() {
    let m = MyBox::new(String::from("Rust"));
    hello(&m); // MyBox<String> 作为&str : 解引用强制多态
    // 这里使用 &m 调用 hello 函数,其为 MyBox<String> 值的引用。因为示例 15-10 中在 MyBox<T> 上实现了 Deref trait,Rust 可以通过 deref 调用将 &MyBox<String> 变为 &String。标准库中提供了 String 上的 Deref 实现,其会返回字符串 slice,这可以在 Deref 的 API 文档中看到。Rust 再次调用 deref 将 &String 变为 &str,这就符合 hello 函数的定义了。
    // hello(&(*m)[..]); 如果没有解引用强制多态的写法
    // (*m) 将 MyBox<String> 解引用为 String。接着 & 和 [..] 获取了整个 String 的字符串 slice 来匹配 hello 的签名}
Rc<T> 引用计数智能指针  Rc::clone 会增加引用计数// 为了启用多所有权,Rust 有一个叫做 Rc<T> 的类型。其名称为 引用计数(reference counting)的缩写。引用计数意味着记录一个值引用的数量来知晓这个值是否仍在被使用。如果某个值有零个引用,就代表没有任何有效引用并可以被清理。RefCell<T> 在运行时检查借用规则,代表其数据的唯一的所有权// 对于引用和 Box<T>,借用规则的不可变性作用于编译时。对于 RefCell<T>,这些不可变性作用于 运行时。对于引用,如果违反这些规则,会得到一个编译错误。而对于 RefCell<T>,如果违反这些规则程序会 panic 并退出。// 当创建不可变和可变引用时,我们分别使用 & 和 &mut 语法。对于 RefCell<T> 来说,则是 borrow 和 borrow_mut 方法,这属于 RefCell<T> 安全 API 的一部分。borrow 方法返回 Ref 类型的智能指针,borrow_mut 方法返回 RefMut 类型的智能指针。这两个类型都实现了 Deref 所以可以当作常规引用对待。原理 :// RefCell<T> 记录当前有多少个活动的 Ref<T> 和 RefMut<T> 智能指针。每次调用 borrow,RefCell<T> 将活动的不可变借用计数加一。当 Ref 值离开作用域时,不可变借用计数减一。就像编译时借用规则一样,RefCell<T> 在任何时候只允许有多个不可变借用或一个可变借用note: 选择 Box<T>,Rc<T> 或 RefCell<T> 的理由:Rc<T> 允许相同数据有多个所有者;Box<T> 和 RefCell<T> 有单一所有者。Box<T> 允许在编译时执行不可变或可变借用检查;Rc<T>仅允许在编译时执行不可变借用检查;RefCell<T> 允许在运行时执行不可变或可变借用检查。因为 RefCell<T> 允许在运行时执行可变借用检查,所以我们可以在即便 RefCell<T> 自身是不可变的情况下修改其内部的值。// 循环引用与内存泄漏 : 弱引用 Weak<T> // 强引用代表如何共享 Rc<T> 实例的所有权。弱引用并不代表所有权关系。他们不会造成引用循环,因为任何引入了弱引用的循环一旦所涉及的强引用计数为 0 就会被打破。use std::rc::{Rc, Weak};
use std::cell::RefCell;#[derive(Debug)]struct Node {    value: i32,    parent: RefCell<Weak<Node>>,    children: RefCell<Vec<Rc<Node>>>,
}

fn main() {
    let leaf = Rc::new(Node {        value: 3,        parent: RefCell::new(Weak::new()),        children: RefCell::new(vec![]),
    });

    println!(        "leaf strong = {}, weak = {}",        Rc::strong_count(&leaf),        Rc::weak_count(&leaf),
    );

    {
        let branch = Rc::new(Node {            value: 5,            parent: RefCell::new(Weak::new()),            children: RefCell::new(vec![Rc::clone(&leaf)]),
        });

        *leaf.parent.borrow_mut() = Rc::downgrade(&branch);

        println!(            "branch strong = {}, weak = {}",            Rc::strong_count(&branch),            Rc::weak_count(&branch),
        );

        println!(            "leaf strong = {}, weak = {}",            Rc::strong_count(&leaf),            Rc::weak_count(&leaf),
        );
    }

    println!("leaf parent = {:?}", leaf.parent.borrow().upgrade());
    println!(        "leaf strong = {}, weak = {}",        Rc::strong_count(&leaf),        Rc::weak_count(&leaf),
    );
}// 总结Box<T> 有一个已知的大小并指向分配在堆上的数据。Rc<T> 记录了堆上数据的引用数量以便可以拥有多个所有者。RefCell<T> 和其内部可变性提供了一个可以用于当需要不可变类型但是需要改变其内部值能力的类型,并在运行时而不是编译时检查借用规则
并发
1. 如何创建线程来同时运行多段代码?use std::thread;use std::time::Duration;fn main() {
    thread::spawn(|| {        for i in 1..10 {            println!("hi number {} from the spawned thread!", i);
            thread::sleep(Duration::from_millis(1));
        }
    });    for i in 1..5 {        println!("hi number {} from the main thread!", i);
        thread::sleep(Duration::from_millis(1));
    }
}// 通过在闭包之前增加 move 关键字,我们强制闭包获取其使用的值的所有权use std::thread;fn main() {    let v = vec![1, 2, 3];    let handle = thread::spawn(move || {        println!("Here's a vector: {:?}", v);
    });

    handle.join().unwrap();
}2. 消息传递(Message passing)并发,其中通道(channel)被用来在线程间传递消息。// mpsc::channel 函数返回一个元组:第一个元素是发送端,而第二个元素是接收端,// Rust 标准库实现通道的方式意味着一个通道可以有多个产生值的 发送(sending)端,但只能有一个消费这些值的 接收(receiving)端// 一个发送端,一个接收端use std::thread;use std::sync::mpsc;fn main() {    let (tx, rx) = mpsc::channel();

    thread::spawn(move || {        let val = String::from("hi");
        tx.send(val).unwrap(); // send 函数获取其参数的所有权并移动这个值归接收者所有
        // val 此处无效
    });    let received = rx.recv().unwrap();    println!("Got: {}", received);
}
-----//  通过clone来实现多生产者use std::thread;use std::sync::mpsc;use std::time::Duration;fn main() {// --snip--let (tx, rx) = mpsc::channel();let tx1 = mpsc::Sender::clone(&tx);
thread::spawn(move || {    let vals = vec![        String::from("hi"),        String::from("from"),        String::from("the"),        String::from("thread"),
    ];    for val in vals {
        tx1.send(val).unwrap();
        thread::sleep(Duration::from_secs(1));
    }
});

thread::spawn(move || {    let vals = vec![        String::from("more"),        String::from("messages"),        String::from("for"),        String::from("you"),
    ];    for val in vals {
        tx.send(val).unwrap();
        thread::sleep(Duration::from_secs(1));
    }
});for received in rx {    println!("Got: {}", received);
}// --snip--}3. 共享状态(Shared state)并发,其中多个线程可以访问同一片数据。(Mutex<T>)//Mutex<T> 是一个智能指针。更准确的说,lock 调用 返回 一个叫做 MutexGuard 的智能指针。这个智能指针实现了 Deref 来指向其内部数据;其也提供了一个 Drop 实现当 MutexGuard 离开作用域时自动释放锁// 单线程上下文中探索 Mutex<T>的例子use std::sync::Mutex;fn main() { 
    let m = Mutex::new(5);

    {        let mut num = m.lock().unwrap();
        *num = 6;
    }    println!("m = {:?}", m);
}
----// 多线程共享Mutex<T>// 原子引用计数Arc<T>// 为什么不是所有的原始类型都是原子性的?答:原因在于线程安全带有性能惩罚,我们希望只在必要时才为此买单。如果只是在单线程中对值进行操作,原子性提供的保证并无必要,代码可以因此运行的更快use std::sync::{Mutex, Arc};use std::thread;fn main() {    let counter = Arc::new(Mutex::new(0));    let mut handles = vec![];    for _ in 0..10 {        let counter = Arc::clone(&counter);        let handle = thread::spawn(move || {            let mut num = counter.lock().unwrap();

            *num += 1;
        });
        handles.push(handle);
    }    for handle in handles {
        handle.join().unwrap();
    }    println!("Result: {}", *counter.lock().unwrap());
}4. Sync 和 Send trait,他们允许 Rust 的并发保证能被扩展到用户定义的和标准库中提供的类型中。// Send 标记 trait 表明类型的所有权可以在线程间传递.// Rc<T>、裸指针:这是不能 Send 的,因为如果克隆了 Rc<T> 的值并尝试将克隆的所有权转移到另一个线程,这两个线程都可能同时更新引用计数// Sync 标记 trait 表明一个实现了 Sync 的类型可以安全的在多个线程中拥有其值的引用// Rc<T>、Cell<T>、RefCell<T>系列类型不是 Sync 的
高级特性
  • unsafe

    • 允许忽略借用规则,可以同时拥有不可变和可变的指针,或多个指向相同位置的可变指针

    • 不保证指向有效的内存

    • 允许为空

    • 不能实现任何自动清理功能

    • 裸指针 :是可变或不可变的,分别写作 *const T 和 *mut T

    • 调用不安全函数或方法

    • 创建不安全代码的抽象

    • 使用extern 函数调用外部代码

    • 访问或修改静态变量

#![allow(unused_variables)]fn main() {let mut num = 5;let r1 = &num as *const i32;let r2 = &mut num as *mut i32;unsafe {  // 可以在安全代码中 创建 裸指针,只是不能在不安全块之外 解引用 裸指针
    println!("r1 is: {}", *r1);    println!("r2 is: {}", *r2);
}#![allow(unused_variables)]fn main() {unsafe fn dangerous() {}unsafe { // 调用不安全函数需在unsafe块中
    dangerous();
}
}#![allow(unused_variables)]fn main() {use std::slice;fn split_at_mut(slice: &mut [i32], mid: usize) -> (&mut [i32], &mut [i32]) {    let len = slice.len();    let ptr = slice.as_mut_ptr();    assert!(mid <= len);    unsafe {
        (slice::from_raw_parts_mut(ptr, mid),
         slice::from_raw_parts_mut(ptr.offset(mid as isize), len - mid))
    }
}
}// 静态变量:全局变量在 Rust 中被称为 静态(static)变量1. 常量 vs 静态变量 // 1. 静态变量中的值有一个固定的内存地址。使用这个值总是会访问相同的地址。另一方面,常量则允许在任何被用到的时候复制其数据// 2. 常量与静态变量的另一个区别在于静态变量可以是可变的.static mut COUNTER: u32 = 0; // 多个线程访问 COUNTER 则可能导致数据竞争fn add_to_count(inc: u32) {    unsafe {
        COUNTER += inc;
    }
}fn main() {
    add_to_count(3);    unsafe {        println!("COUNTER: {}", COUNTER);
    }
}// 不安全trait声明#![allow(unused_variables)]fn main() {unsafe trait Foo {    // methods go here}unsafe impl Foo for i32 {    // method implementations go here}
}

}

  • 高级生命周期

    • 生命周期子类型(lifetime subtyping),一个确保某个生命周期长于另一个生命周期的方式

    • 生命周期 bound(lifetime bounds),用于指定泛型引用的生命周期

    • trait 对象生命周期(trait object lifetimes),以及他们是如何推断的,以及何时需要指定

    • 匿名生命周期:使(生命周期)省略更为明显

  • 高级trait

  • 高级类型

  • 高级函数与闭包

    • 声明宏 vec![]

    • 过程宏(结构体、枚举) #[derive()]

    • 属性宏

    • 函数宏 println!(),formate!()

关键字

as - 强制类型转换,消除特定包含项的 trait 的歧义,或者对 use 和 extern crate 语句中的项重命名break - 立刻退出循环const - 定义常量或不变裸指针(constant raw pointer)continue - 继续进入下一次循环迭代crate - 链接(link)一个外部 crate 或一个代表宏定义的 crate 的宏变量dyn - 动态分发 trait 对象else - 作为 if 和 if let 控制流结构的 fallbackenum - 定义一个枚举extern - 链接一个外部 crate 、函数或变量false - 布尔字面值 falsefn - 定义一个函数或 函数指针类型 (function pointer type)for - 遍历一个迭代器或实现一个 trait 或者指定一个更高级的生命周期if - 基于条件表达式的结果分支impl - 实现自有或 trait 功能in - for 循环语法的一部分let - 绑定一个变量loop - 无条件循环match - 模式匹配mod - 定义一个模块move - 使闭包获取其所捕获项的所有权mut - 表示引用、裸指针或模式绑定的可变性性pub - 表示结构体字段、impl 块或模块的公有可见性ref - 通过引用绑定return - 从函数中返回Self - 实现 trait 的类型的类型别名self - 表示方法本身或当前模块static - 表示全局变量或在整个程序执行期间保持其生命周期struct - 定义一个结构体super - 表示当前模块的父模块trait - 定义一个 traittrue - 布尔字面值 truetype - 定义一个类型别名或关联类型unsafe - 表示不安全的代码、函数、trait 或实现use - 引入外部空间的符号where - 表示一个约束类型的从句while - 基于一个表达式的结果判断是否进行循环


0
305
上一篇: