X

曜彤.手记

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

吉 ICP 备10004938-2号

《The Rust Programming Language》读书笔记(第 1-6 章)


2021 年的计划事项之一 - 学习 Rust。希望不会打击一个 C++er 的脆弱内心 :(。

Rust Version:1.49 or later

Chapter 1 - Getting Started

  1. (Page:8)可以使用 rustup(Rust 官方的“安装管理” CLI 工具)来管理 Rust 的*安装升级,以及本地文档**的使用。也可以使用 *rustfmt格式化 Rust 源代码,以统一为一致的编码风格。
  2. (Page:11)Rust 的基本编码风格是“4 空格缩进”,应用基本结构如下:
fn main() {
    println!("Hello, world!");  // 这里的 println! 是一个宏(!),而非函数;
}
  1. (Page:13)使用 Cargo:

- 创建、编译、运行及检查 Rust 项目

cargo new <project_name>
cargo build  # compile.
cargo run  # compile & run.
cargo check  # check, but not compile into binary (faster than aboves).
cargo build --release  # building for release.
cargo update  # update all the dependent crates.
cargo doc --open  # show help documents of all the deps.

- Cargo.toml 配置文件格式

[package]
name = "hello_cargo"
version = "0.1.0"
authors = ["Becavalier <yhorg@hotmail.com\>"]
edition = "2018"

[dependencies]

Chapter 2 - Programming a Guessing Game

  1. (Page:36)完整示例代码:

- Rust

use std::io;  // use the standard lib (IO).
use std::cmp::Ordering;
use rand::Rng;

fn main() {
    println!("Guess the number!");

    let secret_number = rand::thread_rng()
        .gen_range(1, 101);  // [lower-bound, upper-bound).

    loop {
        println!("Please input your guess:");

        // the variable "guess" is mutable, bounding to a new String value.
        let mut guess = String::new();

        // "read_line" accepts a reference to a mutable String value.
        io::stdin()
            .read_line(&mut guess)  // will return a "io::Result" value.
            .expect("Failed to read line.");

        // convert string to number.
        // shadow the previous value of "guess" with a new one.
        let guess: u32 = match guess.trim().parse() {  // parse into a numerical value.
            Ok(num) => num,
            Err(_) => continue,
        };

        // string template with placeholders.
        println!("You guessed: {}", guess);

        match guess.cmp(&secret_number) {  // "secret_number" would be inferred as u32.
            // several amrs (patterns).
            Ordering::Less => println!("Too small!"),  
            Ordering::Greater => println!("Too big!"),
            Ordering::Equal => {
                println!("You win!");
                break;
            },
        }  
    }
}

- Cargo.toml

[package]
name = "guessing_game"
version = "0.1.0"
authors = ["Becavalier <yhorg@hotmail.com>"]
edition = "2018"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
rand = "0.5.5"

Chapter 3 - Common Programming Concepts

  1. (Page:37)默认情况下,Rust 中定义变量是不可变的(immutable)。可以使用 mut 关键字标记定义的变量,以使其在未来可变。
  2. (Page:39)可以使用 const 关键字定义常量,此时变量名必须使用大写形式,并且需要显式指明类型Github Discussion)。其基本语义与 C++ 中的 constexpr 类似,属于 compile-time 常量。
const MAX_POINTS: u32 = 100_000; 
  1. (Page:40)Shadowing(遮蔽):重新定义同名变量,以覆盖先前的定义(可以更改值类型、可变性)。
fn main() { 
    let spaces = "   ";
    let spaces = spaces.len();
    println!("{}", spaces);  // 3.
}
  1. (Page:42)值类型(默认均放于栈上):

- 标量类型:表示一个单一值。

fn main() { 
    let num_a = 1_000;  // 1000.
    let num_b = 0xffu8;  // 255.
    let num_c = 0o77u32;   // 63.
    let num_d = 0b000111;  // 7.
    let num_e = b'A';  // 65.
    println!("{}, {}, {}, {}, {}", num_a, num_b, num_c, num_d, num_e);

    // deal with wrapping.
    println!("{} {} {} {}", 
        200u8.wrapping_add(57),  // 1.
        200u8.overflowing_add(57).0,  // (1, true) -> 1.
        if 200u8.checked_add(57) == None { "overflow" } else { "not overflow" },
        200u8.saturating_add(57),  // 255 (bound to the edge values).
    );
}
fn main() { 
    let x = 2.8;
    let y: f32 = 3.9;
    println!("{} {}", x, y);
}
fn main() { 
    let x = true;  // one byte in size.
    let y: bool = false;
    println!("{} {}", x, y);
}
fn main() { 
    let x = 'z';  // fixed four bytes in size.
    let y: char = '中';
    println!("{} {} {}", x, y, y as u32);
}

- 复合类型:将多个值进行聚合,成为一个类型。

fn main() { 
    let x: (i32, f64, u8) = (500, 6.4, 1);
    let y = (27, "YHSPY");
    let (age, name) = y;  // destructuring the couple. 
    println!("{} {} {}", age, name, x.0);
}
fn main() { 
    let x = [1, 2, 3];
    let y: [i32; 5] = [1, 2, 3, 4, 5];
    let _z = [3; 5];  // equals to [3, 3, 3, 3, 3].
    println!("{} {} {}", x[0], y[0], y[1]);
}
  1. (Page:50)函数
fn main() { 
    println!("{}", add(1, 2));
    let y = {
      let x = 3;
      x + 1
    };
    println!("{}", y);  // 4.
}
fn add(x: i32, y: i32) -> i32 {
    x + y  // or "return x + y;".
}
  1. (Page:58)控制流
fn main() {
    let condition = true;
    if condition {  // the condition should be a "bool" value.
        println!("condition was true");
    } else {
        println!("condition was false");
    }
    // the "if" and "else" blocks should return the same type.
    let number = if condition { 1 } else { 0 };
    println!("{}", number);
}
fn main() {
    let mut counter = 0;
    let result = loop {
        counter += 1;
        println!("{}", counter);
        if counter == 10 {
            break counter;
        }
    };
    println!("Exit with {}!", result);
}
fn main() {
    let mut counter = 0;
    while counter != 10 {
        counter += 1;
        println!("{}", counter);
    };
    println!("Exit with {}!", counter);
}
fn main() {
    let arr = [1, 2, 3, 4, 5];
    for element in arr.iter() {
        println!("{}", element);
    }
    for number in (1..4).rev() {  // use Range.
        println!("{}", number);
    }
}

Chapter 4 - Understanding Ownership

  1. (Page:67)Ownership(所有权):

- String 在赋值时的默认语义(move)

fn main() {
    // allocate a String on the heap.
    let s1 = String::from("hello");
    let s2 = s1;
    println!("{}", s2);  // str_x is invalid at this point.
}

- String 的拷贝语义(clone)

fn main() {
    let s1 = String::from("hello");
    let s2 = s1.clone();  // use the clone version.
    println!("{} {}", s1, s2);
}

- Ownership 在函数调用与返回间传递

fn main() {
    // gives_ownership moves its return value into _s1.
    let _s1 = gives_ownership();
    let s2 = String::from("hello");  // s2 comes into scope.
    // s2 is moved into takes_and_gives_back, which also moves its return value into _s3.
    let _s3 = takes_and_gives_back(s2);
} 
// Here, _s3 goes out of scope and is dropped. 
// s2 goes out of scope but was moved, so nothing happens. 
// _s1 goes out of scope and is dropped.
fn gives_ownership() -> String { // gives_ownership will move its return value into the function that calls it.
    // some_string comes into scope.
    let some_string = String::from("hello"); 
    // some_string is returned and moves out to the calling function.
    some_string 
}
// takes_and_gives_back will take a String and return one.
fn takes_and_gives_back(a_string: String) -> String {  // a_string comes into scope.
    // a_string is returned and moves out to the calling function.
    a_string
}

- 引用和借用(Reference & Borrowing)

fn main() {
    let mut x = 10;
    let mut k = 10;
    let mut y = &mut x;
    *y = 100;
    y = &mut k;
    *y = 100;
    println!("x = {}, k = {}", &x, &k);
}
fn main() {
    let s1 = String::from("hello");
    let len = calculate_length(&s1);
    println!("The length of '{}' is {}.", s1, len);
}
// the parameter takes a reference to a String object.
fn calculate_length(s: &String) -> usize {
    s.len()
}
fn main() {
    let mut s = String::from("hello");
    let r1 = &mut s;
    let r2 = &mut s;
    println!("{}, {}", r1, r2);  // !!! error occurs !!!

    let mut a_s = String::from("world");
    let ar1 = &a_s;
    let ar2 = &a_s;
    let ar3 = &mut a_s;  // !!! error occurs !!!
    println!("{}, {}, {}", ar1, ar2, ar3);
}
fn first_word(s: &str) -> &str {  // accpet both String and String Slices.
    let bytes = s.as_bytes();
    for (i, &item) in bytes.iter().enumerate() { 
        if item == b' ' {
            return &s[0..i]; 
        }
    }
    &s[..] 
}
  1. (Page:94)Struct
struct User {
    username: String,
    email: String,
    sign_in_count: u64,
    active: bool,
}
fn main() {
    // own all its data by the instance itself.
    let user = build_user(
        String::from("someone@example.com"), 
        String::from("YHSPY"));
    println!("{} {} {} {}", user.username, user.email, user.active, user.sign_in_count);
    let _another_user = User {
        email: String::from("another@example.com"),
        ..user  // use struct update syntax.
    };
}
fn build_user(email: String, username: String) -> User { 
    User {
        email,  // use the field init Shorthand.
        username, 
        active: true, 
        sign_in_count: 1,
    } 
}
struct Years(i64);
struct Days(i64);  // tuple struct.

impl Years {
    fn to_days(&self) -> Days {
        Days(self.0 * 365)
    }
}
impl Days {
    // truncates partial years.
    fn to_years(&self) -> Years {
        Years(self.0 / 365)
    }
}
fn old_enough(age: &Years) -> bool {
    age.0 >= 18
}
fn main() {
    let age = Years(5);
    let age_days = age.to_days();
    println!("Old enough {}", old_enough(&age));
    println!("Old enough {}", old_enough(&age_days.to_years()));
    // cannot accept Days(i64) here.
    // println!("Old enough {}", old_enough(&age_days));
}
fn main() {
    struct Empty();
}
#[derive(Debug)]  // derive an implementation of the triat Debug.
struct Rectangle {
    width: u32,
    height: u32,
}
fn main() {
    let rect = Rectangle {
        width: 10,
        height: 20,
    };
    println!("rect is {:#?}", rect);
    /**
        rect is Rectangle {
            width: 10,
            height: 20,
        }
     */ 
}
#[derive(Debug)]  // derive an implementation of triat Debug.
struct Rectangle {
    width: u32,
    height: u32,
}
impl Rectangle {  // implementation block (can be multiple).
    fn area(&self) -> u32 {
        self.width * self.height
    }
    fn can_hold(&self, other_rect: &Rectangle) -> bool {
        other_rect.width <= self.width && other_rect.height <= self.height
    }
    // associated function (don't take self).
    fn square(size: u32) -> Rectangle {
        Rectangle {
            width: size, height: size
        }
    }
}
fn main() {
    let rect = Rectangle {
        width: 10,
        height: 20,
    };
    let ref_rect = &rect;
    println!("{}", (&ref_rect).area());  // work as expected as well.
    // benefit from the "automatic referencing and dereferencing".
    println!("{}", ref_rect.area());
    println!("{}", rect.can_hold(&(
        Rectangle {
            width: 20,
            height: 20,
        })));
    let square = Rectangle::square(10);
    println!("{:#?}", square);
}

Chapter 6 - Enums and Pattern Matching

  1. (Page:114)Enum
#![allow(dead_code)]
fn main() {
    /* basic usage */
    #[derive(Debug)]
    enum IpAddrKind {
        V4, V6,
    }
    #[derive(Debug)]
    struct IpAddr {
        kind: IpAddrKind,
        address: String,
    }
    println!("{:#?}", IpAddr {
        kind: IpAddrKind::V4,
        address: String::from("127.0.0.1"),
    });

    /* associated values (attach data to each variant of the enum) */
    #[derive(Debug)]
    enum IpAddrEnum {
        V4(String),
        V6(String),
        V4Detailed(u8, u8, u8, u8),  // associated multiple values to an enum.
    }
    println!("{:#?}\n{:#?}\n{:#?}", 
        IpAddrEnum::V4(String::from("127.0.0.1")),
        IpAddrEnum::V6(String::from("::1")),
        IpAddrEnum::V4Detailed(127, 0, 0, 1));

    /* a complicated case */
    #[derive(Debug)]
    enum Message {
        Quit,
        Move { x: i32, y: i32 },
        Write(String),
        ChangeColor(i32, i32, i32),
    }
    // enums can also have methods.
    impl Message {
        fn call(&self) {
            println!("called by {:#?}", self);
        }
    }
    // enum method is called by different member object.
    (Message::Move { x: 10, y: 20 }).call();
    (Message::Write(String::from("YHSPY"))).call();
}
#![allow(dead_code)]
#![allow(unused_variables)]
// use pattern matching.
#[derive(Debug)]
enum UsState {
    Alabama,
    Alaska,
}
#[derive(Debug)]
enum Coin {
    Penny,
    Nickel,
    Dime,
    Quarter(UsState),
}
fn value_in_cents(coin: Coin) -> u8 {
    // will return a u8 number.
    match coin {  // the condition expression can be of any types.
        Coin::Penny => 1,  // arms.
        Coin::Nickel => 5,
        Coin::Dime => 10,
        Coin::Quarter(state) => {
            // "state" will be binded to the passing param.
            println!("State quarter from {:?}!", state);
            25
        },
    }
}
fn main() {
    let some_number = Some(5);
    let absent_number: Option<i32> = None;
    if some_number.is_some() {  // check the status of an Option.
        println!("{}", &some_number.unwrap());
    }
    value_in_cents(Coin::Quarter(UsState::Alabama));

    // matching with Option<T>.
    match some_number {
        None => None,
        Some(i) => {
            println!("the option value is {}", i);
            Some(i + 1)
        }
    };

    // use _ placeholder (match the rest of other choices).
    let some_u8_value = 0u8;
    match some_u8_value {
        1 => println!("one"),
        3 => println!("three"),
        5 => println!("five"),
        _ => (),  // unit value, nothing will happen.
    }
}
#![allow(dead_code)]
#![allow(unused_variables)]
fn main() {
    let some_u8_value = Some(0u8);
    match some_u8_value {
        Some(3) => println!("three"),
        _ => (),
    }
    // with "if-let".
    if let Some(i) = some_u8_value {  // the same as in a match.
        println!("three {}", i);
    } else {
        println!("others");
    }
}


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