rust语言

· 11066 words · 23 minute read

google comprehensive-rust 4days 🔗

较短时间4 days入门。
摘要: standard library

  • Option, Result, error handling
  • String
  • Vec
  • HashMap, config hash algorithm
  • Box: an owned pointer for heap-alloc data
  • Rc: a shared reference counted pointer for heap-alloc data

重要的traits
Iterator , IntoIterator(for in) From Into convert type, Drop.

shared state 共享内存的状态

  • Arc: share read-only access via clone()
  • Mutex: allow mutable access to T behind a read-only interfz
  • Arc<Mutex<Vec<i32>>>

Send & Sync

  • T: send, T can move to thread. T alloc at t1 and de-alloc at t2
  • T: Sync, &T can move to thread. only if &T: Send.
  • Send + Sync
    • 大部分类型都是 i32, char, &str, String, Arc, Mutex AtomicBool
  • Send + !Sync
    • interior mutability
    • mpsc::Sender, mpsc::Receiver
    • Cell RefCell
  • !Send + Sync
    • MutexGuard, alloc at t1 and should de-alloc at t2
  • !Send + !Sync
    • Rc
    • *const T, *mut T

《The Rust Programming Language》 🔗

比较全面,看完需要较长时间

1 开始(开发环境,文档) 🔗

  • rustup.rs安装rustc, cargo, rustfmt, etc...
  • rustup update 更新rustc版本, rustc --version
  • rustup doc // offline book
  • rustup docs --book 会打开file:///Users/brett/.rustup/toolchains/nightly-aarch64-apple-darwin/share/doc/rust/html/book/index.html
  • IDE support via rust-analyzer
  • docs.rs 可以看std库的api
  • cargo --version, cargo check(make sure it compiles and not gen exefile)

看书时的小彩蛋, 3种螃蟹🦀️图标具有特殊含义(不能编译,运行时panic,不是预期行为)。

cargo 🔗

cargo home: 在$HOME/.cargo/中存储项目的依赖。 其中,/bin:cargo install安装的程序。 /git: 对github仓库的依赖。 /registry: 对crate.io仓库的依赖。

Cargo.toml清单的格式。[workspace]: 集成多个package一起共享相同的Cargo.lock,相同的output。[profiles]: 更改编译器的参数, overflow-checks, lto (link time optimizations), incremental增量编译。

2 project:动手猜数小游戏。 🔗

use rand::Rng;
use std::cmp::Ordering;
use std::io;
fn main() {
    let secret_number = rand::thread_rng().gen_range(1..=100);
    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,
            Err(_) => continue,
        };
        println!("You guessed: {guess}");
        match guess.cmp(&secret_number) {
            Ordering::Less => println!("Too small!"),
            Ordering::Greater => println!("Too big!"),
            Ordering::Equal => {
                println!("You win!");
                break;
            }
        }
    }
}

String是标准库中定义的类型
String::new()表示new是String类型上实现的关联函数(associated function)。
注意点: 自动导入标准库中的包
io::stdin() 返回std::io::Stdin结构体。 read_line返回enum Result结构体,需要处理潜在的错误,可以使用expect。如果Result<T, E>类型的值是Ok成功,返回T。如果失败,程序在此处panic崩溃。

rand提供生成随机数的功能
use rand::Rng; 为什么需要导入Rng?
gen_range()方法在trait Rng中定义。如果要使用trait中的方法,trait必须导入到scope中。

如果不知道依赖的crate该怎么使用?cargo doc --open

3 和其他语言类似的概念 🔗

  • 变量
    • 可变性。 变量默认immutable不可变
    • constants。声明时不用let用const。必须指明类型。const HOUR_IN_SECONDS: u32 = 60 * 60;
    • shadowing。变量遮挡。
  • 数据类型
    • rust是静态类型语言,编译期需要知道类型
    • 基本类型u32, i32, isize,usize, char。。。
    • 复合类型tuple(fix size,diff type,anomy), array(fix size,same type),struct(),enum。。。
  • 函数
  • 注释
    • 普通注释
    • 文档注释
  • 控制流, if else, for, while, loop, break, continue, goto。

4 所有权ownership 🔗

ownership, a feature that makes Rust different from other languages. 
make memory safety guarantees without needing a garbage collector,

how Rust lays data out in memory.

stack & heap -> data on stack must fix-sized.

&str (on text seg) vs String (on heap, mutable).
String类型的变量是智能指针在stack上,包含3部分(ptr to heap addr, len, capacity)。

shallow copy: copying the data on the pointer, length, and capacity without copying the data on heap deep copy

    let s1 = String::from("hello");
    let s2 = s1; // 不仅仅是浅拷贝了s1, 而且invalid s1。 称为move。
    let s1 = String::from("hello");
    let s2 = s1.clone(); //  deeply copy
    println!("s1 = {}, s2 = {}", s1, s2);

trait Copy在栈上存储的数据类型实现了Copy trait, 需要move时执行的是copy。例如: 栈上的基本类型数组也实现了Copy trait

    let x = 5;
    let y = x; // Stack-Only Data: deep and shallow copying is same.
    println!("x = {}, y = {}", x, y);
Rust won’t let us annotate a type with Copy if the type, or any of its parts, has implemented the Drop trait.

持有所有权的变量离开scope时,释放内存drop ,与C++的Resource Acquisition Is Initialization (RAII)类似。

ownership & function.

borrowing == reference
rust中所用的参数传递都是传值。 引用实现了copy trait。 按照copy语义,引用会复制一份交给调用的函数。 [char; 2] is Copy (as is any [T; N] where T: Copy).
[string; 2]不能被自动copy。 borrow checker, . calculate overlap

ref &, deref *

slices == [T] -> DST, 单单切片类型[T]是不知道长度的,所以&[T]生成智能指针(ptr + len)就可以使用了
数组的切片,字符串的切片都是类似。 &[i32;9], &String, &str, &Vec ...

A slice is a kind of reference, let you reference a contiguous sequence of elements in a collection
    let ch = "北京".chars().nth(0).unwrap();
    println!("{ch}");
    let chbyte = "北京".bytes().nth(0).unwrap();
    println!("{chbyte}");
    let s = String::from("hello world");
    let hello = &s[0..5];
    let world = &s[6..11];
    let s = String::from("你好")
    let ni = &s[0:2]; // error, 2不是[utf-8的字符边界]。

utf-8的字符边界

let string: String = String::from("hello");
println!("string addr = {:?}", string.as_ptr());
println!("string len = {:?}", string.len());
println!("string cap = {:?}", string.capacity());
        // String == struct{ptr, len, cap} on stack.
println!("{:x?}", unsafe {
            std::mem::transmute::<String, [u8; std::mem::size_of::<String>()]>(string)
        });

        // &str == struct{ptr, len}
let str = "hello";
println!("str addr = {:?}", str.as_ptr());
println!("str len = {:?}", str.len());
        // println!("str cap = {:?}", str.capacity());
println!("{:x?}", unsafe {
            std::mem::transmute::<&str, [u8; std::mem::size_of::<&str>()]>(str)
        });
fn first_word(s: &String) -> &str {
    let bytes = s.as_bytes();

    for (i, &item) in bytes.iter().enumerate() {
        if item == b' ' {
            return &s[0..i];
        }
    }

    &s[..]
}

string Literals are Slices

    let s &str= "Hello, world!"; //&str is an immutable reference. a slice pointing to that specific point of the binary.
fn first_word(s: &String) -> &str {}
fn first_word(s: &str) -> &str {} //  both &String values and &str values can call

5结构体struct 🔗

3类结构体。 struct A {}; struct B (); struct C;

tuple struct

    struct Color(i32, i32, i32);
    let black = Color(0, 0, 0);

    struct AlwaysEqual;
    let subject = AlwaysEqual; //unit-like structs. useful for implement a trait on some type, don’t have any data to store in the type

struct中的field拥有所有权时,field对象的生命周期 == struct对象的生命周期。
struct中的field中type是String, 或者type是&str。
当field是引用类型时,struct类型需要保证struct对象的生命周期 <= 依赖的引用类型对象的生命周期。所以需要引入lifetime标识符。

struct User<'a> {
    active: bool,
    username: &'a str,
    email: &'a str,
    sign_in_count: u64,
}
struct Rectangle {
    width: u32,
    height: u32,
}
let scale = 2;
let rect1 = Rectangle {
        width: 30,
        height: dbg!(scale * 10), // dbg! return the ownership of the expression’s value and print expression
    };
println!("rect1 is {}", rect1);  // error[E0277]: `Rectangle` doesn't implement `std::fmt::Display`
println!("rect1 is {:?}, {:#?}", rect1, rect1); //error[E0277]: `Rectangle` doesn't implement `Debug`
// solved: #[derive(Debug)]
dbg!(&rect1);

automatic referencing and dereferencing.
Rust automatically adds in &, &mut, or * to matches the signature of the method.

6枚举体enum 和 模式匹配 🔗

枚举类型的值范围是固定可枚举的。模式匹配。Option, match, if let。

enum Option<T> {
    None,
    Some(T),
}

模式匹配 match需要fill all arms, exhaustive patterns。如果只关心其中一个pattern,使用if let while let

默认的匹配是绑定操作, 会转移所有权。
以引用方式去匹配, ref可以在匹配时获得一个变量的引用

7 Project, package,crate, module 🔗

如何组织rust项目的代码?

  • Packages: A Cargo feature that lets you build, test, and share crates, Cargo.toml
    • 一个package下可有多个bin crates, src/main.rs; 如果有src/bin有rs文件,each file will be a separate binary crate
    • 但最多一个lib crates, src/lib.rs。
  • Crates: A tree of modules that produces a library or executable, src/main.rs or src/lib.rs
  • Modules and use: Let you control the organization, scope, and privacy of paths +
  • Paths: A way of naming an item, such as a struct, function, or module +

8 常见的集合 🔗

  • vector 动态扩容数组
  • String 字符的集合,字符串
  • HashMap

其他数据集合

Vec 🔗

std::vec::Vec

    // create
    let v: Vec<i32> = Vec::new();
    let v = vec![1, 2, 3];
    // infer the type
    // Updating a Vector
    let mut v = Vec::new();
    v.push(4);v.push(5);v.push(6);
    // Reading Elements of Vectors
         // via indexing or using the get method
    let third: &i32 = &v[2];
    let third: Option<&i32> = v.get(2);
    match third {
        Some(third) => println!("The third element is {third}"),
        None => println!("There is no third element."),
    }
    // Iterating over the Values in a Vector
    let v = vec![100, 32, 57];
    for i in &v { // immutable reference to each element , type of i is &i32
        println!("{i}");
    }
    let mut v = vec![100, 32, 57];
    for i in &mut v { // mutable reference, type of i is &mut i32
        *i += 50;     // * dereference operator to get to the value in i
    }

    //Using an Enum to Store Multiple Types
    enum SpreadsheetCell {
        Int(i32),
        Float(f64),
        Text(String),
    }
    let row = vec![
        SpreadsheetCell::Int(3),
        SpreadsheetCell::Text(String::from("blue")),
        SpreadsheetCell::Float(10.12),
    ];
    // using trait object to store in vector
}

more details at “The Rustonomicon”。

UTF-8 encoding in String 🔗

在core包中只定义了str类型,以引用的形式出现&str。
在std包中定义了String类型,a growable, mutable, owned, UTF-8 encoded string type。 []byte 。A String is a wrapper over a Vec

    // create
    let mut s = String::new();
    let s = "initial contents".to_string(); // string literals  implements the Display trait (define to_string)
    let s = String::from("initial contents");

    // Updating a String
    let mut s = String::from("foo");
    s.push_str("bar");
    s.push('l');
    // Concatenation with the + Operator or the format! Macro
    let s1 = String::from("Hello, ");
    let s2 = String::from("world!");
        // fn add(self, s: &str) -> String ... coerce the &String argument into a &str
    let s3 = s1 + &s2; // note s1 has been moved here and can no longer be used
    let s = format!("{s2}{s3}");
    // Indexing into Strings
        // error....`String` cannot be indexed by `{integer}`  UTF-8中有效字符不是一个字节,index at i 可能是乱码
    let s1 = String::from("hello");
    let h = s1[0];
    // Slicing Strings
    let hello = "Здравствуйте";
    let s = &hello[0..4];  //s == "3"
    let s = &hello[0..1];  //panic, `1` is not a `char boundary`; it is inside 'З' (bytes 0..2) 

    // Methods for Iterating Over Strings
    for c in "Зд".chars() {
        println!("{c}"); // З \n д \n
    }
    for b in "Зд".bytes() {
        println!("{b}"); // 208 \n 151 \n 208 \n 180
    }   

HashMap<K, V> 🔗

    //create
    // not ofen use, so not in the prelude
    use std::collections::HashMap;
    let mut scores = HashMap::new();
    scores.insert(String::from("Blue"), 10);
    scores.insert(String::from("Yellow"), 50);
    // standard lib less support, no built-in macro to construct them.

    // Accessing Values in a Hash Map
    let team_name = String::from("Blue");
    let score = scores.get(&team_name).copied().unwrap_or(0);
    for (key, value) in &scores {
        println!("{}: {}", key, value);
    }
    // Hash Maps and Ownership
    // Adding a Key and Value Only If a Key Isn’t Present
    scores.entry(String::from("Yellow")).or_insert(50);
    // Updating a Value Based on the Old Value
    let text = "hello world wonderful world";
    let mut map = HashMap::new();
    for word in text.split_whitespace() {
        let count = map.entry(word).or_insert(0); //or_insert method returns a mutable reference (&mut V) to the value
        *count += 1;
    }

    //

其他数据集合 🔗

链表。


9 错误处理 🔗

recover from an error or to stop execution?

unrecoverable errors panic! 🔗

panic!

//RUST_BACKTRACE=1 cargo run
// RUST_BACKTRACE=full cargo run
fn main() {
    let v = vec![1, 2, 3];
    v[99];
}

recoverable errors Result<T, E> 🔗

enum Result<T, E> {
    Ok(T),
    Err(E),
}
    use std::fs::File;
    use std::io::ErrorKind;
    let greeting_file_result = File::open("hello.txt");
    let greeting_file = match greeting_file_result {
        Ok(file) => file,
        Err(error) => match error.kind() {
            ErrorKind::NotFound => match File::create("hello.txt") {
                Ok(fc) => fc,
                Err(e) => panic!("Problem creating the file: {:?}", e),
            },
            other_error => {
                panic!("Problem opening the file: {:?}", other_error);
            }
        },
    };
use std::fs::File;
use std::io::ErrorKind;
fn main() {
    let greeting_file = File::open("hello.txt").unwrap_or_else(|error| {
        if error.kind() == ErrorKind::NotFound {
            File::create("hello.txt").unwrap_or_else(|error| {
                panic!("Problem creating the file: {:?}", error);
            })
        } else {
            panic!("Problem opening the file: {:?}", error);
        }
    });
}

Shortcuts for Panic on Error: unwrap() and expect("infos")

Propagating Errors: ?
? after a Result value. From trait and from function.
? after Option value.

use std::fs::File;
fn main() {
    let greeting_file = File::open("hello.txt")?;
}
use std::error::Error;
use std::fs::File;
fn main() -> Result<(), Box<dyn Error>> {
    let greeting_file = File::open("hello.txt")?;
    Ok(())
}

the ? operator in a function that must returns Result, Option, or another type that implements FromResidual
main的return type implement the std::process::Termination trait

10 泛型,trait,生命周期 🔗

生命周期。 即引用的有效作用域。

泛泛的数据类型 🔗

concrete types VS generic types -> reduce code duplication.
Monomorphization is the process of turning generic code into specific code by filling in the concrete types that are used when compiled.

// in function
fn largest<T: std::cmp::PartialOrd>(list: &[T]) -> &T {
    let mut largest = &list[0];

    for item in list {
        if item > largest {
            largest = item;
        }
    }
    largest
}
// in struct
struct Point<T, U> {
    x: T,
    y: U,
}
// in enum
enum Option<T> {
    Some(T),
    None,
}
enum Result<T, E> {
    Ok(T),
    Err(E),
}

trait 定义通用、共享的行为 🔗

trait为类型定义通用、共享的行为。trait也可以限制泛型类型。

// 定义trait类型
trait A {
    fn foo(&self);
    // 可以有默认的函数实现
    fn bar(&self) -> String {
        String::from("bar")
    }
}
// 在类型Type_B上实现trait A
impl A for Type_B {
    fn foo(&self) {...};
}
// trait作为参数
fn C(item: &impl A) {
    //accepts any type that implements the specified trait A
    println!("hi, i have implement trait A, {}", item.bar());
}
//  trait bound;
fn D<T: A>(item: T) {
    println!("{}", item.bar());
}

// Returning Types that impl trait. must returning a single implement trait type
// especially useful in the context of closures and iterators,

//为实现了Display trait的类型实现ToString trait
impl<T: Display> ToString for T {
    // --snip--
}
let s = 3.to_string(); //任何实现了Display的类型的对象可调用to_string方法。

检查引用(借用)有效性--生命周期 🔗

Borrow Checker
为了避免dangling references。 Lifetime Annotations in function Definitions
Lifetime Annotations in Struct Definitions
lifetime elision rules

如何计算每个变量的生命周期, a_ref的life time[4 - 9]-> a life time [code 2 - 10] (区间是包含关系) ✅

x的'a > y的'b, x = &y (❌)
x的'a <= y的'b, x = &y (yes)

let s: &'static str = "I have a static lifetime.";
fn main() {
    let s1 = "hi";
    let s2 = "world";
    let largest = get_largest(s1, s2);
    println!("largest: {}", largest);
}
fn get_largest<'a>(s1: &'a str, s2: &'a str) -> &'a str{
    if s1.len() > s2.len() {
        return s1
    }
    s2
}

&'static and T: 'static

&'static类型的值出现的两种方式

let a = "hello,world";
let b = Box::leak(Box::new(a));

Generic Type Parameters, Trait Bounds, and Lifetimes Together look a little complex, but all features happen at the compile time, no effect on runtime time.

11 测试 🔗

[type checking and borrow checking]不能检查程序的内部逻辑的正确性,所以引入testing。
rust testing中有好用的annotations和macros,以及如何组织单元测试,集成测试。

#[test]将函数变为测试函数。

$cargo test 
running 1 test
test tests::it_works ... ok

test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

   Doc-tests adder

running 0 tests

test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s


0 ignored -> #[should_ignore]
0 measured ->  for benchmark tests 
0 filtered out -> pass an argument to the cargo test command to run special tests 

Doc-tests adder -> documentation tests.

assert_eq!, assert_ne! macros 用到了 operators == and !=, 所以参数需要实现PartialEq trait。 同时fail后,需要打印Debug format的信息,也需要实现Debug trait。基本类型已经实现了这2个trait。struct 和 enum类型需要自行实现,或者#[derive(PartialEq, Debug)]。

cargo test的一些参数选项可以控制单测的执行。
show the output of successful tests: cargo test -- --show-output.
Runn Single Tests: cargo test single_func_name
Run only the ignored tests: cargo test -- --ignored

如何组织测试代码?unit tests and integration tests

#[cfg(test)] 只会在cargo test下执行,避免拖慢编译速度。 tests/下 放集成测试文件,不需要#[cfg(test)]。 cargo test的测试执行顺序是 tests/ 集成测试 -> 单元测试 -> 文档测试。

12 project: io code 🔗

done

13 函数编程语言的特点: Iterator, closure 🔗

闭包 🔗

匿名函数,携带环境变量。

FnOnce (grand parent) -> FnMut (parent) -> Fn (child)

impl<T> Option<T> {
    fn unwrap_or_else(&self, f: F) -> T 
        where F: FnOnce() -> T {
        match self {
            Some(x) => x,
            None => f(), //all closures implement FnOnce
        }
    }
}
let a: Option<Vec<T>> = None;
a.unwrap_or_else(Vec::new); 
#[derive[Debug]]
struct Rectangle {
    width: u32,
    height: u32,
}
let mut arr = [
    Rectangle{width: 10, height: 1},
    Rectangle{width: 3, height: 5},
    Rectangle{width: 7, height: 12},
];
let mut num_sort_operations = 0;
arr.sort_by_key(|r| {
    num_sort_operations += 1;
    r.width}); //uses FnMut

迭代器 🔗

很多迭代器的函数参数都是闭包。

let v = vec![1, 2, 3];
let v_iter = v.iter(); //lazy and do nothing
for item in v_iter {
    println!("Got: {item}");
}
v_iter.next(); //error

所有的迭代器实现了trait Iterator。

trait Iterator {
    type Item;
    fn next(&mut self) -> Option<Self::Item>;
    ...
}
let v1 = vec![1 , 2, 3];
// v1.into_iter(), v1.iter_mut()
let mut v1_iter = v1.iter(); // should mut, for item in v_iter take Ownership
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);
// sum()
let total: i32 = v1_iter.sum(); // take ownership
// iterator adaptor map(), returns a new iterator
let v1: Vec<i32> = vec![1, 2, 3];
v1.iter().map(|x| x + 1); //lazy
// filter()
// collect()
let v2: Vec<_> = v1.iter().map(|x| x + 1).collect();

loop vs iterator

14 cargo & crates.io 🔗

The Cargo Book

Cargo.toml 🔗

[profile.dev]
opt-level = 0
[profile.release]
opt-level = 3

发布到crates.io 🔗

文档注释, cargo doc --open 查看本项目的文档注释对应的html文档。

/// # Examples 
/// # Panics
/// # Safety
/// # Errors
// 文档测试
//! 

Exporting a Convenient Public API with pub use
use my_crate::some_module::another_module::UsefulType; -> use my_crate::UsefulType;
在一个library A的src/lib.rs中pub use A::B::C::D; 可以在其他项目中使用use A::D导入到scope中。

在crates.io上创建账号并获取token。 cargo login token11222
更改toml文件中的cargo publish

workspace组织项目 🔗

一个workspace下管理多个文件夹,共享同一个Cargo.lock和output target目录
例如一个workspace下包含一个bin和2个libraries。workspace: add -> (bin: adder, lib: add_one, add_two)

$ mkdir add
$ cd add
// touch and edit Cargo.toml
[workspace]
members = ["adder"]
// under the add workspace directory
$ cargo new adder
     Created binary (application) `adder` package
$ cargo build 

现在workspace add目录下的结构:
├── Cargo.lock ├── Cargo.toml ├── adder │ ├── Cargo.toml │ └── src │ └── main.rs └── target

[workspace]
members = [
    "adder",
    "add_one",
]
// under the add workspace directory
$ cargo new add_one --lib
     Created library `add_one` package

├── Cargo.lock ├── Cargo.toml ├── add_one │ ├── Cargo.toml │ └── src │ └── lib.rs ├── adder │ ├── Cargo.toml │ └── src │ └── main.rs └── target

edit add_one/src/lib.rs

// add_one/src/lib.rs
pub fn add_one(x: i32) -> i32 {
    x + 1
}

在adder crate中增加对add_one lib crate的依赖。

Filename: adder/Cargo.toml

[dependencies]
add_one = { path = "../add_one" }

edit Filename: adder/src/main.rs

use add_one;

fn main() {
    let num = 10;
    println!("Hello, world! {num} plus one is {}!", add_one::add_one(num));
}

在workspace add目录下:

$ cargo run -p adder
    Finished dev [unoptimized + debuginfo] target(s) in 0.0s
     Running `target/debug/adder`
Hello, world! 10 plus one is 11!

由于workspace下的crate共享了Cargo.lock。all crates are using the same version of all dependencies

// add_one/Cargo.toml
[dependencies]
rand = "0.8.3"

如需要publish workspace下的crate到crate.io,需要publish -p 分开发布。

从crates.io上安装软件 🔗

cargo install 方面从crates.io上安装工具,并安装在$HOME/.cargo/

$ cargo install ripgrep
    Updating crates.io index
  Downloaded ripgrep v11.0.2
  Downloaded 1 crate (243.3 KB) in 0.88s
  Installing ripgrep v11.0.2
--snip--
   Compiling ripgrep v11.0.2
    Finished release [optimized + debuginfo] target(s) in 3m 10s
  Installing ~/.cargo/bin/rg
   Installed package `ripgrep v11.0.2` (executable `rg`)
$ rg --help

扩展cargo的子命令 🔗

如果在$PATh下有一个‘cargo-something’的exe,能够像cargo subcommand方式运行,‘cargo something’。 cargo --list

15 智能指针 🔗

通过struct实现,包含指针+meta信息,struct也实现Deref, Drop trait。 引用仅仅只是借用数据。指针指针可以own数据。
String, Vec是智能指针。

  • Box allocatie value of type T on heap,单所有权
  • Rc enables multiple ownership 多所有权, only immutable borrow
  • Ref, RefMut, RefCell, 单所有权 the borrowing rules at runtime
let b = Box::new(5);
// implements the Deref trait, which allows Box<T> values to be treated like references
println!("b = {}", b);
println!("deref b = {}",*b); // *b == *(b.deref())  deref() 返回的是item的引用。
//必须使用Box才能定义这些类型。
enum List {
    Cons(i32, List), // fail compile。 (1, (2, (3, Nil)))
    Nil,
}
    // 使用Box定义带有递归的类型(有一个字段的类型和自己有相同的类型)。 
enum List {
    Cons(i32, Box<List>),
    Nil,
}
let list = Cons(1, Box::new(Cons(2, Box::new(Cons(3, Box::new(Nil))))));

//因为smart pointer实现Deref trait,可以compiler做 dereference operator * 进而与普通引用一样使用

// deref coercion, &String -> &str, 
fn hello(name: &str) {
    println("hello: {name}");
}
let m = Box::new(String::from("Yellow"));
//&Box<String> --Box impl Defref--> &String --String impl Defref--> &str --> type matching.
// or manually, &(*m)[..] 
hello(&m);

deref coercion

  • from &T to &U when T: Deref<Target=U>
  • from &mut T to &mut U when T: DefrefMut<Target=U>
  • from &mut T to &U when T: Defref<Target=U>

std::mem::drop在prelude里, drop(x) 可提前释放内存。

Rc 引用计数的智能指针

let a = Cons(5, Box::new(Cons(10, Box::new(Nil))));
let b = Cons(3, Box::new(a)); // Box::new() take ownership
let c = Cons(4, Box::new(a)); // failed, use of moved value: `a`

instead

use std::rc::Rc; //not in the prelude
enum List {
    Cons(i32, Rc<List>),
    Nil,
}
let a = Rc::new(Cons(5, Rc::new(Cons(10, Rc::new(Nil)))));
let b = Cons(3, Rc::clone(&a));
let c = Cons(4, Rc::clone(&a));
println!("after clone, rc count = {}", Rc::strong_count(&a));
// immutable
//Drop trait decreases the reference count on  Rc<T> value

RefCell
mutate data even when there are immutable references to that data, use unsafe code inside
在编译期间检查是保守的,可能会reject一些正确的程序。RefCell类型的变量在runtime时进行借用检查,失败会panic。
interior mutability: an immutable type exposes an API for mutating an interior value.

#[derive(Debug)]
enum List {
    Cons(Rc<RefCell<i32>>, Rc<List>),
    Nil,
}

use crate::List::{Cons, Nil};
use std::cell::RefCell;
use std::rc::Rc;

fn main() {
    let value = Rc::new(RefCell::new(5));

    let a = Rc::new(Cons(Rc::clone(&value), Rc::new(Nil)));

    let b = Cons(Rc::new(RefCell::new(3)), Rc::clone(&a));
    let c = Cons(Rc::new(RefCell::new(4)), Rc::clone(&a));

    *value.borrow_mut() += 10;

    println!("a after = {:?}", a);
    println!("b after = {:?}", b);
    println!("c after = {:?}", c);
//  a after = Cons(RefCell { value: 15 }, Nil)
//  b after = Cons(RefCell { value: 3 }, Cons(RefCell { value: 15 }, Nil))
//  c after = Cons(RefCell { value: 4 }, Cons(RefCell { value: 15 }, Nil))
}

reference cycles -> leak memory

use crate::List::{Cons, Nil};
use std::cell::RefCell;
use std::rc::Rc;

#[derive(Debug)]
enum List {
    Cons(i32, RefCell<Rc<List>>),
    Nil,
}

impl List {
    fn tail(&self) -> Option<&RefCell<Rc<List>>> {
        match self {
            Cons(_, item) => Some(item),
            Nil => None,
        }
    }
}

fn main() {
    let a = Rc::new(Cons(5, RefCell::new(Rc::new(Nil))));

    println!("a initial rc count = {}", Rc::strong_count(&a));
    println!("a next item = {:?}", a.tail());

    let b = Rc::new(Cons(10, RefCell::new(Rc::clone(&a))));

    println!("a rc count after b creation = {}", Rc::strong_count(&a));
    println!("b initial rc count = {}", Rc::strong_count(&b));
    println!("b next item = {:?}", b.tail());

    if let Some(link) = a.tail() {
        *link.borrow_mut() = Rc::clone(&b);
    }

    println!("b rc count after changing a = {}", Rc::strong_count(&b));
    println!("a rc count after changing a = {}", Rc::strong_count(&a));

    // Uncomment the next line to see that we have a cycle;
    // it will overflow the stack
    // println!("a next item = {:?}", a.tail());
}

weak reference, Rc::downgrade, Weak

use std::cell::RefCell;
use std::rc::Rc;

#[derive(Debug)]
struct Node { // 树上节点的数据结构
    value: i32,
    children: RefCell<Vec<Rc<Node>>>,
}
fn main() {
    let leaf = Rc::new(Node {
        value: 3,
        children: RefCell::new(vec![]),
    });

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

#[derive(Debug)]
struct Node {
    value: i32,
    parent: RefCell<Weak<Node>>,
    children: RefCell<Vec<Rc<Node>>>,
}
use std::cell::RefCell;
use std::rc::{Rc, Weak};

#[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![]),
    });
    // leaf parent = None
    println!("leaf parent = {:?}", leaf.parent.borrow().upgrade());

    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);

    //leaf parent = Some(Node { value: 5, parent: RefCell { value: (Weak) },
        //children: RefCell { value: [Node { value: 3, parent: RefCell { value: (Weak) },
        //children: RefCell { value: [] } }] } })

    println!("leaf parent = {:?}", leaf.parent.borrow().upgrade());
}

use std::cell::RefCell;
use std::rc::{Rc, Weak};

#[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),
    ); //leaf strong = 1, weak = 0

    {
        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),
        ); //branch strong = 1, weak = 1

        println!(
            "leaf strong = {}, weak = {}",
            Rc::strong_count(&leaf),
            Rc::weak_count(&leaf),
        ); // leaf strong = 2, weak = 0
    }
    //leaf parent = None
    println!("leaf parent = {:?}", leaf.parent.borrow().upgrade());
    println!(
        "leaf strong = {}, weak = {}",
        Rc::strong_count(&leaf),
        Rc::weak_count(&leaf),
    ); //leaf strong = 1, weak = 0
}

16 并发 🔗

无畏并发: 并发问题在编译期间暴露,而为运行时出现后再去花时间去复现,因此让用户无畏惧并发。

创建线程 🔗

use std::thread;
use std::time::Duration;
fn main() {
    thread::spawn(||{
        println!("i am a thread");
        thread::sleep(Duration::from_millis(1);
    })
     thread::sleep(Duration::from_millis(2));
}
use std::thread;
use std::time::Duration;
fn main() {
    let v = vec![1, 2, 3];
    let handle = thread::spawn(move || {
        println!("Here's a vector: {:?}", v);
    });
    handle.join().unwrap();
}

线程间的通信,message-passing concurrency,via channel 🔗

     use std::{
        sync::mpsc::{self, Sender},
        thread,
        time::Duration,
    };
    fn test_channel() {
        let (tx, rx) = mpsc::channel();
        // let tx1 = tx.clone();
        let tx1 = 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);
        }
    }

线程间共享内存(共享状态), shared-state concurrency 🔗

已经可以通过channel来通信,另外一种方式是通过共享内存来通信。
channel的方式类似于单一所有权,数据被发送给channel后,所有权就转移到channel上
共享内存的方式类似多个所有权,多个线程可以访问。

Mutex控制单一访问共享的内存

    use std::sync::Mutex;
    let m = Mutex::new(5);
    {
        let mut num = m.lock().unwrap();
        *num = 6;
    }
    println!("m = {:?}", m); //m = Mutex { data: 6, poisoned: false, .. }
use std::sync::{Mutex, Arc};
use std::sync::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(||{
            let mut num = counter.lock().unwrap(); //Mutex<T> provides interior mutability
            *num += 1;
        })
        handles.push(handle);
    }
    for handle in handles {
        handle.join().unwrap();
    }
    println!("counter:{}", *counter.lock().unwrap());
}

std::sync::atomic

Sync, Send trait 🔗

std::marker::Send trait
实现了Send的类型的值的所有权可以在线程中转移。 大多rust中的类型都是Send的,也有例外,Rc

std::marker::Sync

标准库中提供的类型。

把rustc对并发安全的保证扩展到用户自定义的类型上。

17 rust在面向对象编程上的特点 🔗

18 模式和匹配 patterns and matching 🔗

19 advance features 🔗

  • Unsafe Rust: static analysis is conservative.
  • Advanced traits:
  • Advanced types
  • Advanced functions and closures: function pointers and returning closures
  • Macros:

unsafe rust 🔗

  • 解引用裸指针 *const T and *mut T
  • 调用不安全的方法
  • todo

advance trait 🔗

trait是用到定义共享的行为的。

#![allow(unused)]
fn main() {
    trait Add<Rhs=Self> {
        type Output;
        fn add(self, rhs: Rhs) -> Self::Output;
    }
}

advance type 🔗

类型系统有两个基本的保障: 需要知道一个类型的所有值占多少空间。一个类型下的所有值占用相同的空间。
str, dyn trait, slice 都是DST。不能直接使用这种类型。如果允许,let s1 str = "aa"; let s2 str = "bbb"; 那么str类型下的s1和s2占不同的空间,违背类型系统的保障。所有DST类型在语言中都是以引用的形式(智能指针)出现。 &str -> the address of the str and its length.

str -> &str, Box, Rc trait -> &dyn Trait, Box, Rc

Macros 🔗

20 project: web server 🔗

thread, Arc, Mutex, channel...

其他附录 🔗

源码结构 🔗

image

项目组织 🔗

引入第三方依赖crate 🔗

例如在toml文件中引入rand crate后,cargo build的输出,Cargo.lock的内容。

module 🔗

package, crate, module。

模块机制: rust的mod即是命名空间。

use 是optional,可以使用full path 替换,不是必须使用的。

use作用:将其他module的item导入到current namespace,方便访问。 不带{}的mod作用:将module的内容插入到当前file中。

声明:mod 与 pub mod

引入:use 与 pub use

extern crate rand;
use rand::Rng; // in order to use gen_range method in trait `Rng`.
fn main() {
    // gen_range is a method in Rng trait.
    // the basic idea is this: methods are defined on something called `trait`, 
    // and for the method to work, it needs the trait to be in scope.
    let guess = rand::thread_rng().gen_range(1, 101);
    println!("guess={}", guess);
}

工作空间 rust workspace 🔗

image


数据 🔗

堆和栈 🔗

let s = "hello".to_string();
println!("addr={:p}", &s);
// 输出的地址是 栈中s = ptr|len|cap这个结构体的地址,还是ptr中堆上的地址?

let a = "hello"; // a is fatten pointer
let b = a; // copy trait, b is also a fatten pointer 
println!("&a={:p}, &b={:p}", &a, &b);  // different addr on stack 
println!("*a={:p}, *b={:p}", &*a, &*b); // same addr which is on BSS segment

static Data_Seg: u32 = 1;
fn foo () {}

fn main() {
    let hello = "hello".to_string();
    let data = Box::new(1);

    println!("RODATA = {:p}", "hello world");
    println!("Data static var = {:p}", &Data_seg);
    println!("Text seg = {:p}", foo as *const ());

    // String的结构体分配在栈上,其引用指向栈地址
    println!("STACK (&hello) = {:p}", &hello);
    
    // hello持有

    // 通过解引用获取堆上数据,然后取其引用
    println!("HEAP (&*hello) = {:p}", &*hello);
    
    // Box实现了Pointer trait, 无需额外的解引用
    println!("HEAP box impl pointer, {:p}, {:p}", data, &*data);
}

错误处理 🔗

把错误封装在Result<T, E>中。 错误传播 ?。 unwrap()方法只关心成功的返回结果T,若出错,整个程序终止。

迭代器 🔗

for循环可以用于任何实现了IntoIterator trait的数据结构。

代码 🔗

函数与闭包 🔗

接口与虚表 🔗

虚表是每个trait有一份,还是每个对象有一份,还是每个胖指针有一份?

godbolt.org

impl TraitA for TypeB {}。 虚表是每个<Trait, Type>有一份,在编译时生成好在二进制文件里。

let s1 = String::from("hello");
let s2 = String::from("byebye");

let traitObj1: &dyn Display = &s1;
let traitObj2: &dyn Debug = &s1;

let traitObj3: &dyn Display = &s2;
let traitObj4: &dyn Debug = &s2;

let (addr1, vtable1) = unsafe{ transmute::<_, (usize, usize)>(traitObj1 as *const dyn Display) };
let (addr2, vtable2) = unsafe{ transmute::<_, (usize, usize)>(traitObj2 as *const dyn Debug) };
let (addr3, vtable3) = unsafe{ transmute::<_, (usize, usize)>(traitObj3 as *const dyn Display) };
let (addr4, vtable4) = unsafe{ transmute::<_, (usize, usize)>(traitObj4 as *const dyn Debug) };

println!("s1:{:p}, s2: {:p}, main(): {:p}", &s1, &s2, main as *const());

println!("addr1: 0x{:x}, vtable1: 0x{:x}", addr1, vtable1);

// 
assert_eq!(addr1, addr2);
assert_eq!(addr3, addr4);

// String + Display
assert_eq!(vtable1, vtable3);
// String + Debug
assert_eq!(vtable2, vtable4);

🔗

  • 声明宏
  • 过程宏(函数宏 派生宏 属性宏)

expression 🆚 statement 🔗

rust只有两种语句(无返回值),其他的均是表达式(有返回值)。 一种语句是表达式语句。在表达式后加;。一种语句是声明式语句。

let x = (ley y = 5); //error
let mut y = 5;
// y = 6 is expression, return ().
let x = ( y = 6); // x has the value `()`, not 6

  • 所有权与借用, 生命周期
  • ownership, borrowing
fn main() {
    {
        let s1 = String::from("hallo");
        let s2 = s1;
        println!("{},{}", s1, s2); // error[E0382]: borrow of moved value: `s1`.
    }
    {
        let s1 = String::from("hallo");
        let s2 = s1.clone();  //
        println!("{}, {}", s1, s2);
    }
   {
       //reference
       let s1 = String::from("hallo"); // s1 is the owner of data 'hallo'.
       let s2 = &s1;  // s2 is a reference which points to the same data.
       println!("s1={}, s2={}", s1, s2);
   }
   {
       let s1 = String::from("hallo");
       let s2 = &s1; 
       let s3 = &s2; // two readonly reference of 'hallo'
       println!("s1={}, s2={}", s1, s2);
       println!("s3={}, len={}", s3, s3.len());
   }
   {
       //change string
       let mut s1 = String::from("hello"); // mutable borrow
       let s2 = &mut s1;  // mutable borrow occurs
       s2.push('~');
       println!("s2 = {}", s2);  // s2 = hello~
       // can only use the original when the borrowed item is dropped.
       // s2 is dropped, so s1 can use.
       println!("s1 = {}", s1);  // s1 = hello~  
   }
    {
       let mut s1 = String::from("hello"); // mutable borrow
       let s2 = &mut s1;  // mutable borrow occurs
       s2.push('~');
       println!("s2 = {}", s2); 
       println!("s1 = {}", s1); // not compile!
       s2.push('~') 
    }
    {
       let mut s1 = String::from("hello"); // mutable borrow
       let s2 = &mut s1;  // mutable borrow occurs
       s2.push('~');
       println!("s1 = {}", s1); // not compile!
       println!("s2 = {}", s2); 
    }
    {
        let mut words = vec![String::from("hallo"), String::from("español"), String::from("brett")];
        println!("{:?}", words);
        let t = words[1].clone();
        words[1] = words[2].clone();
        words[2] = t;
        println!("{:?}", words);
        // can't take two mutable loans from one vector,
        // just cast them to raw pointers to do the swap.
        words.swap(1, 2); // built-in swap function.
        println!("{:?}", words);
    }

    /*
    std::mem::swap(..) // use unsafe
    pub fn swap(&mut self, a: usize, b: usize) {
        unsafe {
            let pa: *mut T = &mut self[a];
            let pb: *mut T = &mut self[b];
            ptr::swap(pa, pb);
        }
    }
    */
}

智能指针 🔗

Deref trait,

pub trait Deref {
    type Target: ?Sized;
    fn deref(&self) -> &Self::Target;
}

Drop trait

动态分派,静态分派 🔗

静态分派: 靠泛型的单态化实现,编译时确定函数调用。 callee确定,可以inline。 动态分派:靠trait object实现,运行时确定函数调用。

trait类型是DST类型, 在编译时无法确定size。

trait Fly {
    fn fly(&self);
}
// Fly是trait类型,在编译时无法确定size,无法直接作为param 和return。
// 和java,golang的interface有区别
fn test(arg: Fly){} 

// 利用泛型约束T, ok
fn test<T: Fly>(arg: T) {}

trait类型是DST, 实现trait的具体类型可能有多个,指向trait类型的指针(胖)需要记录

use std::raw // module
pub struct TraitObject {
    pub data: *mut(),   // 实现trait的具体对象,对象里不包含指向虚函数table的指针。与C++不同。
    pub vtable: *mut(), // 
}
use std::mem;

struct Bird;
struct Duck;
impl Fly for Bird {
    fn fly(&self) {println!("bird can fly");}
}
impl Fly for Duck {
    fn fly(&self) {println!("duck can fly");}
}

// &Fly, Box<Fly> both trait object
fn print_trait_object(p: &Fly) {
    let (data, vtalbe): (usize, usize) = unsafe{mem::transmute(p)};
    println!("trait object: data={}, vtable={}", data, vtable);
    unsafe{
        let v: *const usize = vtable as * const() as * const usize;
        println!("vtable {}, {}, {}, {}", *v, *v.offset(1), *v.offset(2), *v.offset(3));
    }
}
fn main() {
    let duck Duck;
    let p_duck = &duck;

    let p_fly = p_duck as &Fly;
    /*
    let p_fly = TraitObject{
            data: &p_duck, // store data
            vtable: &Fly_for_Duck_vtable, //store method
    }
    */
    p_fly.fly();
    /*
        (p_fly.vtable.method)(p_fly.data);
    */
    
    println!("size of p_duck={}, size of p_fly={}", mem::size_of_val(p_duck), mem::size_of_val(p_fly));
    let duck_fly: usize = unsafe{mem::transmute(Duck::fly)};
    let bird_fly: usize = unsafe{mem::transmute(Bird::fly)};
    println!("Duck::fly={}, Bird::fly={}", duck_fly, bird_fly);

    print_trait_object(p_fly);
    let bird Bird;
    print_trait_object(&brid as &Fly);
}x

object safe: trait object受到限制。

  • 当trait上有Self:sized限制时,无法构造trait obj。
  • trait里所有函数都是object safe
    • 当trait里函数上有Self: sized限制时,该函数不会加入到vtable中。
    • 当函数中除了self参数之外还有Self类型作为参数或者返回类型时,
    • 当函数有泛型参数时。

closure 🔗

如果一门编程语言,其函数是一等公民,那么它必然会支持闭包(closure),因为函数作为返回值往往需要返回一个闭包。
闭包相当于一个捕获变量的结构体,实现了 FnOnce 或 FnMut 或 Fn。

fn f<F: FnOnce() -> String>(g: F) {
    println!("{}", g());
}

let mut s = String::from("halo");
let t = String::from("world");

f(||{
    s += &t;
    s
});
struct Closure<'a>{
    s: String,
    t: &'a String,
}

impl <'a> FnOnce<()> for Closure<'a> {
    type Output = String;
    fn call_once(self) -> Output{
        self.s += &*self.t;
        self.s
    }
}
f(Closure{s: s, t: &t});

标准库中: Rc, Box::leak() 突破单一所有权
Iterator 不断构造新结构,支持lazy evaluation
Rc::clone 内部可变性。

tree src, readme.md 目录框架

dors.rs <-> code 方便来回切换

从trait开始: docs上required method(需要被实现的方法), provided method, implementations on foreign types, implementor。

从struct开始: methods, trait implementation, auto trait, blanket trait

[Blanket Implementations}(https://doc.rust-lang.org/nightly/src/core/any.rs.html#200)

#[stable(feature = "rust1", since = "1.0.0")]
impl<T: 'static + ?Sized> Any for T {
    fn type_id(&self) -> TypeId {
        TypeId::of::<T>()
    }
}

  T: 'static -> the type does not contain any non-static references。
  T类型是要么拥有所有权,要么是拥有静态引用&str。

blanket trait例如Any, 为泛型T实现了Any trait, 那么任何类型只要满足bound trait

match is designed to work quite well without taking ownership

fn tree_weight_v2(t: &BinaryTree) -> i32 {
    //               ^~~~~~~~~~~ The `&` means we are *borrowing* the tree
   // *t == L-value expression
   match *t { // *t ==  not making a copy of the tree, nor moving it to a new temporary location
        BinaryTree::Leaf(payload) => payload,
        BinaryTree::Node(ref left, payload, ref right) => {
            tree_weight_v2(left) + payload + tree_weight_v2(right)
        }
    }
}
// 当所有权转移时,数据的可变性可能发生改变。
    let immutable_box = Box::new(5u32);
    println!("immutable_box contains {}", immutable_box);
    //*immutable_box = 4;
    // 移动box,改变所有权(和可变性)
    let mut mutable_box = immutable_box;
    println!("mutable_box contains {}", mutable_box);
    *mutable_box = 4;
    println!("mutable_box now contains {}", mutable_box);
}

    // 部分移动
    #[derive(Debug)]
    struct Person {
        name: String,
        age: u8,
    }
    let person = Person {
        name: String::from("brett"),
        age: 19,
    };
    // `name` 从 person 中移走,但 `age` 只是引用
    let Person { name, ref age } = person;
    println!("The person's age is {}", age);
    println!("The person's name is {}", name);
    // 报错!部分移动值的借用:`person` 部分借用产生
    //println!("The person struct is {:?}", person);

    // `person` 不能使用,但 `person.age` 因为没有被移动而可以继续使用
    println!("The person's age from person struct is {}", person.age);

直接访问Vec结构体中的len字段,可以吗? 分两种情况吧,safe rust不行。 unsafe rust下 mem::transmute 应该是可以的。 error[E0616]: field len of struct Vec is private https://doc.rust-lang.org/beta/error_codes/E0616.html

"a".to_string() vs "a".to_owned() 
https://users.rust-lang.org/t/to-string-vs-to-owned-for-string-literals/1441

How to step into std source code when debugging in VS Code?` solved
Source Path Remapping

格式化输出 🔗

{} 适用于 实现了 std::fmt::Display类型。 {:?} 适用于 实现了std::fmt::Debug类型

rust书写的习惯 🔗

类型是驼峰命名。 变量名和函数名是蛇形命名。

参考与资料 🔗

[1] welcome to rust 101
[2] rust by example
[3] rust blog
[4] this week in rust
[5] inside rust
[6] Tour of Rust 以简单的例子串讲,很好入手
[7]《The Rust Programming Language》 done
[8]《Rust Book experiment: Experiment Introduction》 [9] rust noniom
[10] Rust Language Cheat Sheet 适合快速浏览,有问题时查看的工具书 done
[11] rust by example 每篇都很短并配有例子,很好入手。done
[12] google comprehensive-rust 4days
[13] awesome block chain rust
[14]
[15]
[16]
[17]
[18]
[19]
[20]
text editor. https://www.philippflenker.com/hecto pngme book. build your text editor with rust.

                    writing an os in rust.   os.phil-opp.com/zh-CN/     github.com/phil-opp/blog-os