X

曜彤.手记

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

吉 ICP 备10004938-2号

《The Rust Programming Language》读书笔记(第 12-16 章)


书接上回,第 12-16 章的笔记。

Chapter 12 - An I/O Project: Building a Command Line Program

  1. (Page:268)基本代码(Monolithic):

- src/main.rs

// src/main.rs
use std::env;
use std::process;
use minigrep::Config;

fn main() {
    // "unwrap_or_else" allows us to define some non-panic error handling.
    // "env::args()" returns an iterator.
    let config = Config::new(env::args()).unwrap_or_else(|err| {
        // closure (anonymous function).
        eprintln!("Problem parsing arguments: {}", err);
        process::exit(1);
    });
    if let Err(e) = minigrep::run(config) {
        // prints to the standard error stream.
        eprintln!("Application error: {}", e);
        process::exit(1);
    }
}

- src/lib.rs

// src/lib.rs
use std::error::Error;
use std::fs;
use std::env;

// the function will return a type that implements the "Error" trait,
// but we don’t have to specify what particular type the return value will be.
pub fn run(config: Config) -> Result<(), Box<dyn Error>> {
  // propagate errors.
  let contents = fs::read_to_string(config.filename)?;
  let results = if config.case_sensitive {
    search(&config.query, &contents)
  } else {
    search_case_insensitive(&config.query, &contents)
  };
  for line in results {
    println!("{}", line);
  }
  // we’re calling "run" for its side effects only.
  Ok(())
}

pub struct Config {
  pub query: String,
  pub filename: String,
  pub case_sensitive: bool,
}

impl Config {
  pub fn new(mut args: env::Args) -> Result<Config, &'static str> {
      args.next();  // skip the filename.
      let query = match args.next() {
          Some(arg) => arg,
          None => return Err("Didn't get a query string"),
      };
      let filename = match args.next() {
          Some(arg) => arg,
          None => return Err("Didn't get a file name"),
      };
      let case_sensitive = env::var("CASE_INSENSITIVE").is_err();
      Ok(Config {
        query, filename, case_sensitive,
      })
  }
}

pub fn search<'a>(query: &str, contents: &'a str) -> Vec<&'a str> {
    contents.lines().filter(|line| line.contains(query)).collect()
}

pub fn search_case_insensitive<'a>(
  query: &str, 
  contents: &'a str,
) -> Vec<&'a str> {
  // will return a String, rather than string slice.
  let query = query.to_lowercase();
  let mut results = Vec::new();
  // "lines()" returns an iterator.
  for line in contents.lines() {
    if line.to_lowercase().contains(&query) {
      results.push(line);
    }
  }
  results
}

#[cfg(test)]
mod tests {
  use super::*;

  #[test]
  fn one_result() {
    let query = "duct";
    let contents = "\
Rust:
safe, fast, productive.
Pick three.
Duct tape.";
    assert_eq!(vec!["safe, fast, productive."], search(query, contents));
  }

  #[test]
  fn case_insensitive() {
    let query = "rUsT";
    let contents = "\
Rust:
safe, fast, productive.
Pick three.
Trust me.";
    assert_eq!(
      vec!["Rust:", "Trust me."], 
      search_case_insensitive(query, contents)
    );
  }
}
  1. (Page:)TDD(Test-Driven Development)的基本实施步骤:

Chapter 13 - Functional Language Features: Iterators and Closures

  1. (Page:288)闭包(Closure):
fn add_one_v1 (x:u32) -> u32 { x + 1 }  // function definition.
let add_one_v2 = |x: u32| -> u32 { x + 1 };  // the rests are closure definition.
let add_one_v3 = |x| { x + 1 }; 
let add_one_v4 = |x| x + 1 ;
fn main() {
    let x = vec![1, 2, 3];
    let equal_to_x = move |z| z == x;  // the value of "x" has been moved into the closure.
    let y = vec![1, 2, 3];
    assert!(equal_to_x(y));
}
// memoization.
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
            }
        }
    }
}
  1. (Page:303)迭代器(Iterator):
fn main() {
    let v1 = vec![1, 2, 3];
    let v1_iter = v1.iter();  // return an iterator for v1.
    for val in v1_iter {
        println!("{}", val);
    }
}
fn main() {
    let mut x = vec![1, 2, 3];
    let v1_iter = x.iter();
    for &val in v1_iter {
        // got immutable references to the values inside "x".
        println!("{}", val);
    }
    let mut v2_iter = x.iter_mut();
    if let Some(v) = v2_iter.next() {  // pattern matching.
        // got mutable references.   
        *v = 100;  // dereference and assign.
    }
    let v3_iter = x.into_iter();
    for val in v3_iter {
        // take the ownership of the values.
        println!("{}", val);
    }
    // println!("{:#?}", x);  // panic!.
}
fn main() {
    let v1: Vec<i32> = vec![1, 2, 3];
    // the iterator adaptor is lazy.
    let v2: Vec<_> = v1.iter().map(|x| x + 1).collect();
    println!("{:#?}", v2);
}
#[derive(PartialEq, Debug)]
struct Shoe {
    size: u32,
    style: String,
}

fn shoes_in_my_size(shoes: Vec<Shoe>, shoe_size: u32) -> Vec<Shoe> {
    // capture the surrounding parameter.
    shoes.into_iter().filter(|s| s.size == shoe_size).collect()
}
struct Counter {
    count: u32,
}
impl Counter {
    fn new() -> Counter {
        Counter { count: 0 }
    }
}
impl Iterator for Counter {
    type Item = u32;  // the iterator will return u32.
    fn next(&mut self) -> Option<Self::Item> {
        if self.count < 5 {
            self.count += 1;
            Some(self.count)
        } else {
            None
        }
    }
}
fn main() {
    let counter = Counter::new();
    let c = counter.iter();
    let counter_v: Vec<_> = counter.map(|x| x + 1).collect();
    println!("{:#?}", counter_v);
}

Chapter 14 - More About Cargo and Crates.io

  1. (Page:320)自定义发布和开发时选项
[profile.dev]  # from 1 to 3.
opt-level = 0

[profile.release] 
opt-level = 3
  1. (Page:321)文档注释///
/// Adds one to the number given.
/// # Examples
///
/// ```
/// let arg = 5;
/// let answer = my_crate::add_one(arg); ///
/// assert_eq!(6, answer);
/// ```
/// # Panics
/// // describe the exposed functions that may cause panic.
/// # Errors
/// // describe the exposed functions that returns Result.
/// # Safety
/// // describe the exposed functions which are unsafe.
pub fn add_one(x: i32) -> i32 {
    x + 1 
}
  1. (Page:323)用于整体描述模块内容的注释类型:
// src/lib.rs
//! # My Crate
//!
//! `my_crate` is a collection of utilities to make performing certain //! calculations more convenient.
/// Adds one to the number given.
  1. (Page:329)可以使用 pub use 重新整理导出 API 接口的引用结构。
  2. (Page:330)为将要发布的 crate 添加元信息
[package]
name = "guessing_game"
version = "0.1.0"
authors = ["Becavalier "]
edition = "2018"
description = "A fun game where you guess what number the computer has chosen."
license = "MIT OR Apache-2.0"
  1. (Page:332)使用 cargo yank --vers <version> 阻止某一版本 crate 被新的项目使用;反之:cargo yank --vers <version> --undo
  2. (Page:333)Cargo Workspace
[workspace]
# specify packages.
members = [ 
  "adder",
  "add-one", 
]
  1. (Page:339)使用 cargo install 安装来自 Crates.io 的二进制包(默认安装在 $HOME/.cargo/bin),并可在系统的全局环境下使用。

Chapter 15 - Smart Pointers

  1. (Page:341)智能指针(Smart Pointer):
  1. (Page:342)Box<T>
fn main() {
    let mut b = Box::new(5);  // allocated on the heap.
    *b = 10;  // manipulate as a pointer.
    println!("b = {}", b);
}
// "b" will be deallocated before getting here.

enum List {
    Cons(i32, Box<List>),  // save a pointer with a fixed size.
    Nil,
}
use crate::List::{Cons, Nil};
fn main() {
    let list = Cons(1, 
        Box::new(Cons(2, 
            Box::new(Cons(3, 
                Box::new(Nil))))));
}
  1. (Page:348)Deref trait:
fn main() {
    let x = 5;
    let y = &x;  // pointer to the original value.
    let m = Box::new(x);  // pointer to a copied value on the heap.

    assert_eq!(5, *y);
    // assert_eq!(5, y);  // panic!, they are different types.

    let mut n = MyBox::new(x);
    *n = 10;  // mutation.
    assert_eq!(10, *n);  // underlying: *n -> *(n.deref()).
}
struct MyBox<T>(T);  // tuple struct.
impl<T> MyBox<T> {
    fn new(x: T) -> MyBox<T> {
        MyBox(x)
    }
}
use std::ops::Deref;
impl<T> Deref for MyBox<T> {  // &T -> &U.
    type Target = T;  // define an associated type.
    fn deref(&self) -> &T {
        &self.0
    }
}
impl<T> DerefMut for MyBox<T> {  // for mutability. &mut T -> &mut U.
    fn deref_mut(&mut self) -> &mut T {
        &mut self.0
    }
}
fn hello(name: &str) {
    println!("Hello, {}!", name);
}
fn main() {
    let m = MyBox::new(String::from("Rust"));
    hello(&m);
    // hello(&(*m)[..]);  // original way with "Deref Coercion".
}
  1. (Page:354)Drop trait:
struct S {
    data: String,
}
impl Drop for S {
    fn drop(&mut self) {
        println!("Dropping S with data `{}`!", self.data);
    }
}
fn main() {
    let c = S {
        data: String::from("my stuff"),
    };
    let d = S {
        data: String::from("other stuff"),
    };
}
fn main() {
    let c = S {
        data: String::from("my stuff"),
    };
    // std::mem::drop(c); 
    drop(c);  // drop "c" manually.
    println!("S dropped before the end of main.");
}
  1. (Page:358)Rc<T>

enum List {
    Cons(i32, Rc<List>),
    Nil,
}
use crate::List::{Cons, Nil};
use std::rc::Rc;
fn main() {
    let a = Rc::new(Cons(5, 
                Rc::new(Cons(10, 
                    Rc::new(Nil)))));
    let b = Cons(3, Rc::clone(&a));  // increments the reference count.
    let c = Cons(3, Rc::clone(&a));
}
  1. (Page:363)RefCall<T>
  1. (Page:371)结合使用 RefCell<T>Rc<T>
#[derive(Debug)]
enum List {
    Cons(Rc<RefCell<i32>>, Rc<List>),
    Nil,
}
use crate::List::{Cons, Nil};
use std::rc::Rc;
use std::cell::RefCell;
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;  // automatic referencing and dereferencing.
    println!("a after = {:?}", a); 
    println!("b after = {:?}", b); 
    println!("c after = {:?}", c);
}
  1. (Page:373)循环引用

- 一个产生循环引用的例子

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

#[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))));
    let b = Rc::new(Cons(10, RefCell::new(Rc::clone(&a))));
    if let Some(link) = a.tail() { 
        *link.borrow_mut() = Rc::clone(&b);
    }
}

- 借助 Weak<T> 解决循环引用

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()),  // placeholder.
        children: RefCell::new(vec![]),
    });
    let branch = Rc::new(Node {
        value: 5,
        parent: RefCell::new(Weak::new()),  // placeholder.
        children: RefCell::new(vec![Rc::clone(&leaf)]),
    });
    *leaf.parent.borrow_mut() = Rc::downgrade(&branch);  // populating.
    println!("leaf parent = {:#?}", leaf.parent.borrow().upgrade());  // return Option<_, None>.
}

Chapter 16 - Fearless Concurrency

  1. (Page:383)使用线程:
use std::thread;
use std::time::Duration;

fn main() {
    // by default, the new thread will be stopped when the main thread ends.
    // "thread::spawn()" returns a "JoinHandle".
    let handle = 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));
    }
    // let the current thread wait here for the thread to finish.
    handle.join().unwrap();
}
use std::thread;

fn main() {
    let v = vec![1, 2, 3];
    // force the closure to take ownership of the values it’s using.
    let handle = thread::spawn(move || {
        println!("Here's a vector: {:?}", v);
    });
    handle.join().unwrap();
}
  1. (Page:391)Message Passing
use std::thread;
use std::sync::mpsc;  // "multiple producer, single consumer".

fn main() {
    // get the "sending end" (transmitter) and "receiving end" (receiver).
    let (tx, rx) = mpsc::channel();
    thread::spawn(move || {
        let val = String::from("Hi");
        // send a message.
        tx.send(val).unwrap();
    });

    // block the main thread’s execution and wait until a value is sent down the channel.
    let received = rx.recv().unwrap();

    // we can even iterate the receiver directly.
    // for v in rx {  
    //     println!("Got: {}", received);
    // }

    // let received = rx.try_recv().unwrap();  // the non-blocking version, can be used within a loop.
    println!("Got: {}", received);
}
  1. (Page:398)共享状态多线程:
use std::sync::Mutex;

fn main() {
    let m = Mutex::new(5);  // wrap the data inside the Mutex.
    {
        // "lock()" will block the thread until getting the lock.
        // will return a MutexGuard, which is a smart pointer.
        let mut num = m.lock().unwrap();  
        *num = 6;
    }
    // the lock will be released automatically when it goes out of the scope.
    println!("m = {:?}", m);
}
use std::sync::{Mutex, Arc};
use std::thread;

fn main() {
    // use thread-safe Rc -> Arc.
    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());
}
  1. (Page:406)Send trait:
  1. (Page:407)Sync trait:


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