美文网首页
Rust学习笔记(3)

Rust学习笔记(3)

作者: 爱写作的harry | 来源:发表于2019-06-13 23:53 被阅读0次

    内容整理自:https://doc.rust-lang.org/book/title-page.html

    Chapter 5: Using Structs to Structure Related Data

    Define and Instantiating Structs

    • like tuples, can be different types
    • unlike tuples, you can name each data
    • more flexible than tuples
    • definition:
    struct User {
        username: String,
        email: String,
        sign_in_count: u64,
        active: bool,
    }
    
    
    • creating a instance
    let user1 = User {
        email: String::from("someone@example.com"),
        username: String::from("someusername123"),
        active: true,
        sign_in_count: 1,
    };
    
    // assign value to a field, entire instance must be mutable
    user1.email = String::from("anotheremail@example.com");
    
    • use fn to return a new instance with Shorthand
    fn build_user(email: String, username: String) -> User {
        User {
            email,
            username,
            active: true,
            sign_in_count: 1,
        }
    }
    
    • Creating Instances From Other Instances With Struct Update Syntax
    let user2 = User {
        email: String::from("another@example.com"),
        username: String::from("anotherusername567"),
        ..user1
    };
    

    The syntax .. specifies that the remaining fields not explicitly set should have the same value as the fields in the given instance.

    • Using Tuple Structs without Named Fields to Create Different Types
    struct Color(i32, i32, i32);
    struct Point(i32, i32, i32);
    
    let black = Color(0, 0, 0);
    let origin = Point(0, 0, 0);
    

    Tuple structs are useful when you want to give the whole tuple a name and make the tuple be a different type from other tuples, and naming each field as in a regular struct would be verbose or redundant.

    • Unit-Like Structs Without Any Fields
      You can also define structs that don’t have any fields! These are called unit-like structs because they behave similarly to (), the unit type. Unit-like structs can be useful in situations in which you need to implement a trait on some type but don’t have any data that you want to store in the type itself.

    Method Syntax

    • like fn
    • define in struct/enum/trait
    • first param is self
    • definition:
    #[derive(Debug)]
    struct Rectangle {
        width: u32,
        height: u32,
    }
    
    impl Rectangle {
        fn area(&self) -> u32 {
            self.width * self.height
        }
    }
    
    fn main() {
        let rect1 = Rectangle { width: 30, height: 50 };
    
        println!(
            "The area of the rectangle is {} square pixels.",
            rect1.area()
        );
    }
    
    • Methods with More Parameters
    fn main() {
        let rect1 = Rectangle { width: 30, height: 50 };
        let rect2 = Rectangle { width: 10, height: 40 };
        let rect3 = Rectangle { width: 60, height: 45 };
    
        println!("Can rect1 hold rect2? {}", rect1.can_hold(&rect2));
        println!("Can rect1 hold rect3? {}", rect1.can_hold(&rect3));
    }
    
    impl Rectangle {
        fn area(&self) -> u32 {
            self.width * self.height
        }
    
        fn can_hold(&self, other: &Rectangle) -> bool {
            self.width > other.width && self.height > other.height
        }
    }
    
    • Associated Functions
    impl Rectangle {
        fn square(size: u32) -> Rectangle {
            Rectangle { width: size, height: size }
        }
    }
    
    // call
    let sq = Rectangle::square(3);
    

    Chapter 6: Enums and Pattern Matching

    Defining an Enum

    enum IpAddrKind {
        V4,
        V6,
    }
    
    • variant types
    enum Message {
        Quit,
        Move { x: i32, y: i32 },
        Write(String),
        ChangeColor(i32, i32, i32),
    }
    
    • define method
    impl Message {
        fn call(&self) {
            // method body would be defined here
        }
    }
    
    let m = Message::Write(String::from("hello"));
    m.call();
    
    • The Option Enum and Its Advantages Over Null Values
      • a value could be something or it could be nothing
      • Rust doesn’t have the null feature
      • the concept that null is trying to express is still a useful one: a null is a value that is currently invalid or absent for some reason.
      • Option<T>
    enum Option<T> {
        Some(T),
        None,
    }
    
    * use Option
    
    let some_number = Some(5);
    let some_string = Some("a string");
    
    let absent_number: Option<i32> = None;
    

    Everywhere that a value has a type that isn’t an Option<T>, you can safely assume that the value isn’t null

    The match Control Flow Operator

    enum Coin {
        Penny,
        Nickel,
        Dime,
        Quarter,
    }
    
    fn value_in_cents(coin: Coin) -> u8 {
        match coin {
            Coin::Penny => 1,
            Coin::Nickel => 5,
            Coin::Dime => 10,
            Coin::Quarter => 25,
        }
    }
    
    • Patterns that Bind to Values
    #[derive(Debug)] // so we can inspect the state in a minute
    enum UsState {
        Alabama,
        Alaska,
        // --snip--
    }
    
    enum Coin {
        Penny,
        Nickel,
        Dime,
        Quarter(UsState),
    }
    
    fn value_in_cents(coin: Coin) -> u8 {
        match coin {
            Coin::Penny => 1,
            Coin::Nickel => 5,
            Coin::Dime => 10,
            Coin::Quarter(state) => {
                println!("State quarter from {:?}!", state);
                25
            },
        }
    }
    
    • Matching with Option<T>
    fn plus_one(x: Option<i32>) -> Option<i32> {
        match x {
            None => None,
            Some(i) => Some(i + 1),
        }
    }
    
    let five = Some(5);
    let six = plus_one(five);
    let none = plus_one(None);
    
    • The _ Placeholder
    let some_u8_value = 0u8;
    match some_u8_value {
        1 => println!("one"),
        3 => println!("three"),
        5 => println!("five"),
        7 => println!("seven"),
        _ => (),
    }
    

    the _ will match all the possible cases that aren’t specified before it.

    Concise Control Flow with if let

    handle values that match one pattern while ignoring the rest

    let mut count = 0;
    if let Coin::Quarter(state) = coin {
        println!("State quarter from {:?}!", state);
    } else {
        count += 1;
    }
    

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

    Packages and Crates

    $ cargo new my-project
         Created binary (application) `my-project` package
    $ ls my-project
    Cargo.toml
    src
    $ ls my-project/src
    main.rs
    
    • Cargo.thml give us a package
    • src/lib.rs is a library create, src/lib.rs is its crate root

    Defining Modules to Control Scope and Privacy

    cargo new --lib restaurant
    
    // filenam: src/lib.rs
    mod front_of_house {
        mod hosting {
            fn add_to_waitlist() {}
    
            fn seat_at_table() {}
        }
    
        mod serving {
            fn take_order() {}
    
            fn serve_order() {}
    
            fn take_payment() {}
        }
    }
    
    • The module tree for the code:
    crate
     └── front_of_house
         ├── hosting
         │   ├── add_to_waitlist
         │   └── seat_at_table
         └── serving
             ├── take_order
             ├── serve_order
             └── take_payment
    

    Paths for Referring to an Item in the Module Tree

    • Path
      • absolutes
        • use crate
      • relative
        • self
        • super
        • identifier in current module
    mod front_of_house {
        pub mod hosting {
            pub fn add_to_waitlist() {}
        }
    }
    
    pub fn eat_at_restaurant() {
        // Absolute path
        crate::front_of_house::hosting::add_to_waitlist();
    
        // Relative path
        front_of_house::hosting::add_to_waitlist();
    }
    
    • all items in module are private by default, need to use pub keyword
    • Starting Relative Paths with super
    fn serve_order() {}
    
    mod back_of_house {
        fn fix_incorrect_order() {
            cook_order();
            super::serve_order();
        }
    
        fn cook_order() {}
    }
    
    • struct with pub, need to specify which field to be pub
    mod back_of_house {
        pub struct Breakfast {
            pub toast: String,
            seasonal_fruit: String,
        }
    
        impl Breakfast {
            pub fn summer(toast: &str) -> Breakfast {
                Breakfast {
                    toast: String::from(toast),
                    seasonal_fruit: String::from("peaches"),
                }
            }
        }
    }
    
    pub fn eat_at_restaurant() {
        // Order a breakfast in the summer with Rye toast
        let mut meal = back_of_house::Breakfast::summer("Rye");
        // Change our mind about what bread we'd like
        meal.toast = String::from("Wheat");
        println!("I'd like {} toast please", meal.toast);
    
        // The next line won't compile if we uncomment it; we're not allowed
        // to see or modify the seasonal fruit that comes with the meal
        // meal.seasonal_fruit = String::from("blueberries");
    }
    
    • pub with enum, and the default for enum variants is to be public
    mod back_of_house {
        pub enum Appetizer {
            Soup,
            Salad,
        }
    }
    
    pub fn eat_at_restaurant() {
        let order1 = back_of_house::Appetizer::Soup;
        let order2 = back_of_house::Appetizer::Salad;
    }
    

    Bringing Paths into Scope with the use Keyword

    mod front_of_house {
        pub mod hosting {
            pub fn add_to_waitlist() {}
        }
    }
    // absolute path
    use crate::front_of_house::hosting;
    // relative path
    // use self::front_of_house::hosting;
    
    pub fn eat_at_restaurant() {
        hosting::add_to_waitlist();
        hosting::add_to_waitlist();
        hosting::add_to_waitlist();
    }
    
    use crate::front_of_house::hosting::add_to_waitlist;
    
    pub fn eat_at_restaurant() {
        add_to_waitlist();
        add_to_waitlist();
        add_to_waitlist();
    }
    
    • Providing New Names with the as Keyword
    use std::fmt::Result;
    use std::io::Result as IoResult;
    
    fn function1() -> Result {
    }
    
    fn function2() -> IoResult<()> {
    }
    
    • Re-exporting Names with pub use
    mod front_of_house {
        pub mod hosting {
            pub fn add_to_waitlist() {}
        }
    }
    
    pub use crate::front_of_house::hosting;
    
    pub fn eat_at_restaurant() {
        hosting::add_to_waitlist();
        hosting::add_to_waitlist();
        hosting::add_to_waitlist();
    }
    
    • Using Nested Paths to Clean Up Large use Lists
    use std::cmp::Ordering;
    use std::io;
    // same as above
    use std::{cmp::Ordering, io};
    
    // ------
    
    use std::io;
    use std::io::Write;
    // same as above
    use std::io::{self, Write};
    
    • The Glob Operator: bring all public items
    use std::collections::*;
    

    Separating Modules into Different Files

    // Filename: src/lib.rs
    
    mod front_of_house;
    
    pub use crate::front_of_house::hosting;
    
    pub fn eat_at_restaurant() {
        hosting::add_to_waitlist();
        hosting::add_to_waitlist();
        hosting::add_to_waitlist();
    }
    
    // Filename: src/front_of_house.rs
    pub mod hosting;
    
    // Filename: src/front_of_house/hosting.rs
    pub fn add_to_waitlist() {}
    

    Chapter 8: Common Collections

    A vector allows you to store a variable number of values next to each other.

    Storing Lists of Values with Vectors

    • creating
    let v: Vec<i32> = Vec::new();
    let v = vec![1, 2, 3];
    
    • updating
    let mut v = Vec::new();
    
    v.push(5);
    v.push(6);
    v.push(7);
    v.push(8);
    
    • Reading Elements of Vectors
    let v = vec![1, 2, 3, 4, 5];
    
    let third: &i32 = &v[2];
    println!("The third element is {}", third);
    
    match v.get(2) {
        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 {
        println!("{}", i);
    }
    
    • mutable
    let mut v = vec![100, 32, 57];
    for i in &mut v {
        *i += 50;
    }
    

    Storing Keys with Associated Values in Hash Maps

    • Creating a New Hash Map
    use std::collections::HashMap;
    
    let mut scores = HashMap::new();
    
    scores.insert(String::from("Blue"), 10);
    scores.insert(String::from("Yellow"), 50);
    
    • hash maps are homogeneous: all of the keys must have the same type, and all of the values must have the same type.
    • Accessing Values in a Hash Map
    use std::collections::HashMap;
    
    let mut scores = HashMap::new();
    
    scores.insert(String::from("Blue"), 10);
    scores.insert(String::from("Yellow"), 50);
    
    let team_name = String::from("Blue");
    let score = scores.get(&team_name);
    
    • iterate
    use std::collections::HashMap;
    
    let mut scores = HashMap::new();
    
    scores.insert(String::from("Blue"), 10);
    scores.insert(String::from("Yellow"), 50);
    
    for (key, value) in &scores {
        println!("{}: {}", key, value);
    }
    
    • Updating a Hash Map
      • insert twice for a key will replace old value
      • Only Inserting a Value If the Key Has No Value
    use std::collections::HashMap;
    
    let mut scores = HashMap::new();
    scores.insert(String::from("Blue"), 10);
    
    scores.entry(String::from("Yellow")).or_insert(50);
    scores.entry(String::from("Blue")).or_insert(50);
    
    println!("{:?}", scores);   // {"Yellow": 50, "Blue": 10}
    
    • Updating a Value Based on the Old Value
    use std::collections::HashMap;
    
    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);
        *count += 1;
    }
    
    println!("{:?}", map);  // {"world": 2, "hello": 1, "wonderful": 1}
    

    or_insert method returns a mutable reference (&mut V) to the value for this key

    Chapter 9: Error Handling

    Recoverable Errors with panic!

    use std::fs::File;
    
    fn main() {
        let f = File::open("hello.txt");
    
        let f = match f {
            Ok(file) => file,
            Err(error) => {
                panic!("There was a problem opening the file: {:?}", error)
            },
        };
    }
    

    Matching on Different Errors

    use std::fs::File;
    use std::io::ErrorKind;
    
    fn main() {
        let f = File::open("hello.txt");
    
        let f = match f {
            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),
            },
        };
    }
    

    More clean code:

    use std::fs::File;
    use std::io::ErrorKind;
    
    fn main() {
        let f = File::open("hello.txt").unwrap_or_else(|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);
            }
        });
    }
    
    • Shortcuts for Panic on Error: unwrap and expect
      • If the Result value is the Ok variant, unwrap will return the value inside the Ok. If the Result is the Err variant, unwrap will call the panic! macro for us.
      • expect: customize panic! Error message
    use std::fs::File;
    
    fn main() {
        let f = File::open("hello.txt").expect("Failed to open hello.txt");
    }
    
    • Propagating Errors
      • return the error to the calling code
    use std::io;
    use std::io::Read;
    use std::fs::File;
    
    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),
        }
    }
    
    * A Shortcut for Propagating Errors: the ? Operator
    
    use std::io;
    use std::io::Read;
    use std::fs::File;
    
    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)
    }
    

    chaining method calls

    use std::io;
    use std::io::Read;
    use std::fs::File;
    
    fn read_username_from_file() -> Result<String, io::Error> {
        let mut s = String::new();
    
        File::open("hello.txt")?.read_to_string(&mut s)?;
    
        Ok(s)
    }
    
    • The ? Operator Can Only Be Used in Functions That Return Result
    use std::error::Error;
    use std::fs::File;
    
    fn main() -> Result<(), Box<dyn Error>> {
        let f = File::open("hello.txt")?;
    
        Ok(())
    }
    

    Box<dyn Error> mean “any kind of error.” Using ? in a main function with this return type is allowed.

    To panic! or Not to panic!

    • Examples, Prototype Code, and Tests
      • the unwrap and expect methods are very handy when prototyping, before you’re ready to decide how to handle errors
    • Cases in Which You Have More Information Than the Compiler
      • can ensure by manually inspecting the code that you’ll never have an Err variant, it’s perfectly acceptable to call unwrap
    use std::net::IpAddr;
    
    let home: IpAddr = "127.0.0.1".parse().unwrap();
    
    • Guidelines for Error Handling

      • not panic
        • failure is expected, such as hit a rate limit
      • panic
        • operate invalid data
    • Creating Custom Types for Validation

    pub struct Guess {
        value: i32,
    }
    
    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
            }
        }
          // getter
        pub fn value(&self) -> i32 {
            self.value
        }
    }
    

    Chapter 10: Generic Types, Traits, and Lifetimes

    Validating References with Lifetimes

    • Lifetime Annotations in Function Signatures
      • When annotating lifetimes in functions, the annotations go in the function signature, not in the function body.
    fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {
        if x.len() > y.len() {
            x
        } else {
            y
        }
    }
    
    * the lifetime of the reference returned by function is the same as the smaller of the lifetimes of the references passed in. 
    
    • Thinking in Terms of Lifetimes
      • When returning a reference from a function, the lifetime parameter for the return type needs to match the lifetime parameter for one of the parameters.
    • Lifetime Annotations in Struct Definitions
      • It’s possible for structs to hold references, but in that case we would need to add a lifetime annotation on every reference in the struct’s definition.
    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 };
    }
    
    • Lifetime Elision
      • 3 rules
    • The Static Lifetime
      • can live for the entire duration of the program
    let s: &'static str = "I have a static lifetime.";
    
    • Generic Type Parameters, Trait Bounds, and Lifetimes Together
    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
        }
    }
    

    lifetimes are a type of generic, the declarations of the lifetime parameter 'a and the generic type parameter T go in the same list inside the angle brackets after the function name.

    相关文章

      网友评论

          本文标题:Rust学习笔记(3)

          本文链接:https://www.haomeiwen.com/subject/vrdyfctx.html