X

曜彤.手记

随记,关于互联网技术、产品与创业

吉 ICP 备10004938-2号

《The Rust Programming Language》读书笔记(第 7-11 章)


书接上回,第 7-11 章的笔记。

Chapter 7 - Managing Growing Projects with Packages, Crates, and Modules

  1. (Page:125)Rust 的模块系统元素包括:
  1. (Page:125)包和 Crates:
  1. (Page:127)模块
// use std::io;
// use std::io::Write;
use std::io::{self, Write};  // same as above.
use std::collections::*;  // imports all the items under the given path.
// src/lib.rs
#![allow(dead_code)]
#![allow(unused_variables)]
#![allow(unused_mut)]

// load the contents of other module from another file with the same name.
mod other_mod;
mod mod_outter { 
    // export inner function for external use.
    pub use mod_innter_b::bar;
    pub enum Country {
        China,  // the variants belonging to this enum will be public.
        America,
    }
    pub struct Person {
        pub name: String,
        pub country: Country,
        age: u8,
    }
    impl Person {
        pub fn construct_person(name: String, age: u8, country: Country) -> Person {
            Person { name, age, country }
        }
    }
    fn outter_fn() {
        // can refer to sibling item by default.
        mod_innter_a::bar()
    }
    pub mod mod_innter_a {
        pub fn foo() {
           super::outter_fn()
        } 
        pub fn bar() {}
    }
    pub mod mod_innter_b {  // priavte module.
        fn foo() {}
        pub fn bar() {} 
    }
}
// the same as using "crate::" since we are at the crate root level here where the "self" scope is equal to crate.
use self::mod_outter::mod_innter_a;  
use crate::mod_outter::Person as APerson;  // set alias for a path.
pub use crate::mod_outter::mod_innter_b::bar;  
pub fn foo() {
    // call a method exported by "pub use" insde "mod_outter".
    mod_outter::bar();
    // call function with relative path which defined with "use".
    mod_innter_a::foo();
    // same, but absolute path.
    crate::mod_outter::mod_innter_a::foo();
    let mut person = APerson::construct_person(String::from("YHSPY"), 27, mod_outter::Country::China);
    // below line won't compile since "age" is a private field.
    // person.age = 28;
    other_mod::other_sub_mod::foo();  // call function defined in the other module of other file.
}

Chapter 8 - Common Collections

  1. (Page:144)VectorVec<T>
#![allow(dead_code)]
#![allow(unused_variables)]
fn main() {
    let v: Vec<i32> = Vec::new(); 
    // create Vector with macro (type will be inferred by initial values).
    let mut v_by_macro = vec![1, 2, 3];
    v_by_macro.push(4);

    // read elements of Vector.
    let third: &i32 = &v_by_macro[2];  // access by reference.
    match v.get(2) {  // "get" method returns an Option<&T>.
        Some(v) => println!("{}", v),
        _ => (),
    }
    println!("{}", third);

    // iterate in turn.
    for i in &mut v_by_macro {
        *i *= 2;  // deference and operate.
    }
    for i in &v_by_macro {
        println!("{}", i);
    }

    // use heterogeneous enum in Vector.
    #[derive(Debug)]
    enum Gender {
        Female,
        Male,
    }
    #[derive(Debug)]
    struct Person {
        usename: String,
        age: u8,
        gender: Gender,
    }
    #[derive(Debug)]
    enum HeterogeneousEnum {
        NormalPerson(Person),
    }
    let person_vec = vec!(HeterogeneousEnum::NormalPerson(
        Person {
            usename: String::from("YHSPY"),
            age: 27,
            gender: Gender::Male,
        }));
    println!("{:#?}", person_vec);
}
  1. (Page:150)String
#![allow(dead_code)]
#![allow(unused_variables)]
fn main() {
    let data = "Здравствуйте";  // UTF-8 encoded.
    let mut s = data.to_string();  // convert string literal to String object.
    // update String.
    s.push_str("bar");
    s.push('b');  // take a single character.
    println!("{}", s);

    // concatenation with "+".
    let s1 = String::from("Hello, ");
    let s2 = String::from("world!");
    // fn add(self, s: &str) -> String {}
    let s3 = s1 + &s2;  // s1 will be invalided.
    let s = format!("{}-{}", s2, s3);  // return a formatted String and doesn't take any ownership.
    println!("{}", s);

    // slicing Strings.
    let s = &data[0..2];
    println!("{}", s);

    // iterate Strings.
    for c in data.chars() {  // "chars()" returns Unicode scalar values.
        println!("{}", c);
    }
    for c in data.bytes() {  // "chars()" returns each raw bytes.
        println!("{}", c);
    }
}
  1. (Page:158)HashMapHashMap<K, V>
#![allow(dead_code)]
#![allow(unused_variables)]
fn main() {
    use std::collections::HashMap;

    let mut scores = HashMap::new();
    scores.insert(String::from("Blue"), 10);  // store data on the heap.
    scores.insert(String::from("Yellow"), 50);

    // generate a HashMap by "collect()".
    let teams = vec![String::from("Blue"), String::from("Yellow")];
    let initial_socres = vec![10, 50];
    let mut scores: HashMap<_, _> = teams.into_iter().zip(initial_socres.into_iter()).collect();

    // access values in a HashMap.
    let score = scores.get(&String::from("Blue"));  // return Option<&T>.
    match score {
        Some(v) => println!("{}", v),
        _ => (),
    }

    // iterate kv pairs.
    for (k, v) in &scores {
        println!("{} {}", k, v);
    }

    // replace the existing kv pair.
    scores.insert(String::from("Blue"), 20);
    println!("{:#?}", scores);

    // only insert a value if the key has no value.
    let reference_to_inserted_value = scores.entry(  // return a mutable reference.
        String::from("Red")).or_insert(30);
    // update a value based on the old value.
    *reference_to_inserted_value += 1;
    println!("{:#?}", scores);
}

Chapter 9 - Error Handling

  1. (Page:164)Rust 中的两种错误类型:
  1. (Page:165)panic! 宏:
[profile.release] 
panic = 'abort'
fn main() {
    panic!("crash and burn!");
}
#![allow(dead_code)]
#![allow(unused_variables)]
use std::fs::File;
fn main() {
    // returns the contained [Ok] value, or panic.
    let f = File::open("hello.txt").unwrap();
    // returns the contained [Ok] value, or panic with the given message.
    let f = File::open("hello.txt").expect("Failed to open hello.txt");
}
  1. (Page:168)Result<T, E> 类型:
// enum Result<T, E> {
//     Ok(T),
//     Err(E), 
// }
#![allow(dead_code)]
#![allow(unused_variables)]
use std::fs::File;
use std::io::ErrorKind;
fn main() {
    let f = File::open("hello.txt");
    let f = match f {  
        // the Result enum has been brought into the scope by default.
        // Result::<_, _>::Ok(file) => file,
        Ok(file) => file,
        Err(error) => match error.kind() {
            ErrorKind::NotFound => panic!("File not found!"),
            other_error => panic!("{:?}", other_error),
        }
    };
}
  1. (Page:173)错误传播
#![allow(dead_code)]
#![allow(unused_variables)]
use std::fs::File;
use std::io;
use std::io::Read;

fn main() {
    fn foo() -> Option<i32> {
        fn bar() -> Option<i32> {
            return None;
        }
        let num = bar()?;
        Some(num)
    }
    let result_opt = foo();
    if let Some(v) = result_opt {
        println!("{}", v);
    } else {
        println!("Got None.");
    }
}

fn read_username_from_file() -> Result<String, io::Error> { 
    let mut f = File::open("hello.txt")?;  // propagate the error to the caller.
    // 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), 
    // }
    f.read_to_string(&mut s)?;  // propagate the error to the caller.

    // chaining method call with "?.".
    let mut s = String::new();
    File::open("hello.txt")?.read_to_string(&mut s)?;
    Ok(s)
}
  1. (Page:178)main 函数的返回值
fn main() -> Result<(), Box<dyn Error>> {}
  1. (Page:178)何时使用 painc!
mod global {
    #[derive(Debug)]
    pub struct Guess {
        value: i32,  // this field is private by default.
    }
    impl Guess {
        pub fn new(value: i32) -> Guess {
            if value < 1 || value > 100 {
                panic!("Guess value must be between 1 and 100, got {}.", value);
            }
            Guess { value }
        }
        pub fn value(&self) -> i32 {  // a getter.
            self.value
        }
    }
}
fn main() {
    let guess = global::Guess::new(1);
    println!("{:#?}", guess);
}

Chapter 10 - Generic Types, Traits, and Lifetimes

  1. (Page:186)泛型(Generic):
// generic function.
fn foo<T>(list:&[T]) -> &T {}
// generic struct.
struct Point<T, U> {
    x: T,
    y: U,
}

impl<T, U> Point<T, U> where T: Copy, U: Copy {  // types in this line go with the definition of the Point struct itself.
    fn mixup<V, W>(&self, other: Point<V, W>) -> Point<T, W> {  // types in this line are only relevant to the method.
        Point {
            x: self.x,
            y: other.y,
        }
    } 
}
// methods only for generic type f32.
impl Point<f32, f32> {
    fn disance_from_origin(&self) -> f32 {
        (self.x.powi(2) + self.y.powi(2)).sqrt()
    }
}
// generic enum.
enum Option<T> {
    Some(T),
    None,
}
  1. (Page:196)特质(Trait):
// define a trait.
pub trait Summary {
    // fn summarize(&self) -> String;
    fn summarize_author(&self) -> String;
    // trait method with default implementation, can be overridden.
    fn summarize(&self) -> String {
        // a trait method can call other methods defined within the same trait.
        format!("default implementation - {}", self.summarize_author())
    }
}
struct Person {
    username: String,
    age: u8,
}
// implement a trait (no instance methods included).
impl Summary for Person {
    // fn summarize(&self) -> String {
    //     format!("{}-{}", self.username, self.age)
    // }
    fn summarize_author(&self) -> String {
        format!("@{}", self.username)
    }
}
// the "impl Trait" syntax can also be used for return type.
pub fn notify(item: &impl Summary) {
    println!("Breaking news! {}", item.summarize());
}
// genric type style, equivalent to the above one.
// pub fn notify<T: Summary>(item: &T) {
//     println!("Breaking news! {}", item.summarize());
// }
fn main() {
    let p = Person {
        username: String::from("YHSPY"),
        age: 27,
    };
    // call the trait method.
    println!("{}", p.summarize());
    notify(&p);
}
pub fn notify(item: &(impl Summary + Display)) {} 
pub fn notify<T: Summary + Display>(item: &T) {}
fn foo<T, U>(t: &T, u: &U) -> i32
    where T: Display + Clone, U: Clone + Debug {}
use std::fmt::Display;
struct Pair<T> {
    x: T,
    y: T,
}
impl<T> Pair<T> {
    fn new(x: T, y: T) -> Self {
        Self { x, y }
    }
}
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 x = {}", self.y);
        }
    }
}
impl<T: Display> ToString for T {}
  1. (Page:208)LifetimeThe Rust’s most distinctive feature。本身也是一种泛型,可以帮助 Borrow Checker 在编译器检查引用的生命期。

// the borrow checker doesn't know which borrowed value would be returned, so we need to tell it the lifetimes of the returned reference.
// add generic lifetime annotations (the lifetime of the returned reference should be the smaller of the lifetimes of "x" and "y").
fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {
    if x.len() > y.len() {
        // will return a borrowed value.
        x
    } else {
        y
    }
}
fn main() {
    let str1 = String::from("abcd");
    let str2 = "xyz";
    let result = longest(str1.as_str(), str2);
    println!("The longest string is {}", result);
}
// an instance of ImportantExcerpt can't outlive the reference it holds in its "part" field.
struct ImportantExcerpt<'a> {
    part: &'a str,
}
impl ImportantExcerpt {
    fn announce_and_return_part(&self, announcement: &str) -> &str {
        println!("Attention please: {}", announcement);
        self.part  // get the lifetime of "&self" implicitly.
    }
}
// we don't need to specify the lifetime for each parameter here explicitly due to the above rules.
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[..] 
}
// the text of this string is stored directly in the program’s binary, which is always available.
let s: &'static str = "I have a static lifetime.";
  1. (Page:222)同时使用泛型、Trait 以及 Lifetime:
use std::fmt::Display;
fn longest_with_an_announcement<'a, T> (
    x: &'a str,
    y: &'a str,
    ann: T,
) -> &'a str where T: Display, {
    println!("Announcement! {}", ann);
    if x.len() > y.len() {
        x
    } else {
        y
    }
}

Chapter 11 - Writing Automated Tests

  1. (Page:223)如何编写测试脚本
mod lib {
    use std::fmt;
    #[derive(Debug)]
    pub struct Rectangle {
        width: u32,
        height: u32,
    }
    impl Rectangle {
        pub fn can_hold(&self, other: &Rectangle) -> bool {
            self.width > other.width && self.height > other.height
        }
        pub fn new(width: u32, height: u32) -> Rectangle {
            Rectangle { width, height }
        }
    }
    impl fmt::Display for Rectangle {
        fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
            write!(f, "({}, {})", self.width, self.height)
        }
    }
}

// export to external env.
pub use lib::Rectangle; 

#[cfg(test)]
mod tests {
    use super::*;
    #[test]  // indicate the next function is for test.
    fn it_works() {
        assert_eq!(2 + 2, 4);
        assert_ne!(2 + 3, 4);
    }
    #[should_panic(expected = "it panics!")]  // makes a test pass if the code inside the function panics.
    #[test]
    fn it_panics() {
        panic!("it panics!");
    }
    #[test]
    fn larger_can_hold_smaller() {
        let larger = Rectangle::new(8, 7);
        let smaller = Rectangle::new(5, 1);
        // with custom failure messages.
        assert!(larger.can_hold(&smaller), "{}-{}", larger, smaller);
    }
    #[test]
    fn with_result() -> Result<(), String> {  // we can use error-propagation way(?) here.
        if 2 + 2 == 4 {
            Ok(())  // test passed.
        } else {
            Err(String::from("two plus two does not equal four!"))  // test failed.
        }
    }
}
  1. (Page:241)控制测试的运行
#[test]
#[ignore]
fn expensive_test() {}
  1. (Page:248)测试体系:

- 单元测试

- 集成测试

// a case of integration test file.
use my_test_lib::*;

#[test]
fn larger_can_hold_smaller() {
    let larger = Rectangle::new(8, 7);
    let smaller = Rectangle::new(5, 1);
    // with custom failure messages.
    assert!(larger.can_hold(&smaller), "{}-{}", larger, smaller);
}
mod common;
// ...


这是文章底线,下面是评论
  暂无评论,欢迎勾搭 :)