wqpw_blog

= =

View on GitHub
7 October 2019

Rust简单整理(c10-c12)

by wqpw

基于官方教程《The Rust Programming Language》

10. Generic Types, Traits, and Lifetimes

//generic.rs
struct Point<T, U> {
    x: T,
    y: U,
}
impl<T, U> Point<T, U> {
    fn x(&self) -> &T {
        &self.x
    }
    fn mixup<V, W>(self, other: Point<V, W>) -> Point<T, W> {
        Point {
            x: self.x,
            y: other.y,
        }
    }
}
impl Point<f32, f32> {
    fn distance(&self) -> f32 {
        (self.x.powi(2) + self.y.powi(2)).sqrt()
    }
}
fn main() {
    println!("Hello, world!");
}
//rust_trait
mod RustTrait {
    pub trait Summary {
        fn summarize_author(&self) -> String;
        fn summarize(&self) -> String {
            String::from("(Read more...)")
        }
    }
}
use RustTrait::Summary;
pub struct NewsArticle {
    pub headline: String,
    pub location: String,
    pub author: String,
    pub content: String,
}
pub struct Tweet {
    pub username: String,
    pub content: String,
    pub reply: bool,
    pub retweet: bool,
}
impl Summary for Tweet {
    fn summarize(&self) -> String {
        format!("{}: {}", self.summarize_author(), self.content)
    }
    fn summarize_author(&self) -> String {
        format!("@{}", self.username)
    }
}
//impl Summary for NewsArticle {}
impl Summary for NewsArticle {
    fn summarize_author(&self) -> String {
        format!("@{}", self.author)
    }
}
pub fn notify(item: impl Summary) {
    println!("Notice: {}", item.summarize());
}
fn returns_summarizable() -> impl Summary {
    Tweet {
        username: String::from("horse_ebooks"),
        content: String::from("of course, as you probably already know, people"),
        reply: false,
        retweet: false,
    }
}
//pub fn notify<T: Summary>(item1: T, item2: T) {
//pub fn notify(item: impl Summary + Display) {
//pub fn notify<T: Summary + Display>(item: T) {
//fn some_function<T: Display + Clone, U: Clone + Debug>(t: T, u: U) -> i32 {
/*
fn some_function<T, U>(t: T, u: U) -> i32
    where T: Display + Clone,
          U: Clone + Debug
{
*/
fn main() {
    let tweet = Tweet {
        username: String::from("gogogo"),
        content: String::from("of course, ...!"),
        reply: false,
        retweet: false,
    };
    println!("1 new tweet: {}", tweet.summarize());
    let article = NewsArticle {
        headline: String::from("Penguins win the Stanley Cup Championship!"),
        location: String::from("Pittsburgh, PA, USA"),
        author: String::from("Iceburgh"),
        content: String::from("The Pittsburgh Penguins once again are the best
        hockey team in the NHL."),
    };
    println!("New article available! {}", article.summarize());
    let tweet = returns_summarizable();
    println!("{}", tweet.summarize());
}
//lifetime.rs
/*
every reference in Rust has a lifetime, 
which is the scope for which that reference is valid
*/
fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {
/*
 the lifetime of the reference returned by the longest function 
 is the same as the smaller of the lifetimes of the references passed in
 After working through all three rules, 
 we still haven’t figured out what the return type’s lifetime is.
*/
    if x.len() > y.len() {
        x
    } else {
        y
    }
}
struct ImportantExcerpt<'a> {
    part: &'a str,
}
impl<'a> ImportantExcerpt<'a> {
    fn announce_and_return_part(&self, announcement: &str) -> &str {
        println!("Attention please: {}", announcement);
        self.part //rule3: get lifetime of &self
    }
}
fn main() {
    let a = String::from("test");
    let b = "summer";
    println!("{}", longest(a.as_str(), &b));
    println!("{}", longest_with_an_announcement(a.as_str(), &b, "Ahahahaha"));
    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};
    println!("{}", i.part);
    //In the future, even fewer lifetime annotations might be required.
    //lifetime elision rules
/*
 rules apply to fn:
 1: each parameter that is a reference gets its own lifetime parameter
 fn foo<'a, 'b>(x: &'a i32, y: &'b i32)
 2: if there is exactly one input lifetime parameter, 
    that lifetime is assigned to all output lifetime parameters
 fn foo<'a>(x: &'a i32) -> &'a i32
 3: if there are multiple input lifetime parameters, 
    but one of them is &self or &mut self because this is a method, 
    the lifetime of self is assigned to all output lifetime parameters
*/
    let s: &'static str = "I have a static lifetime.";
    //All string literals have the 'static lifetime
    //because it hardcoded in binary
    println!("{}", s);
}
use std::fmt::Display;
fn longest_with_an_announcement<'a, 'b, 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
    }
}

11. Writing Automated Tests

//lib.rs
struct Rectangle {
    width: u32,
    height: u32,
}
impl Rectangle {
    fn can_hold(&self, other: &Rectangle) -> bool {
        self.width > other.width && self.height > other.height
    }
}
struct Guess {
    value: i32,
}
impl Guess {
    pub fn new(value: i32) -> Guess {
        if value < 1 {
            panic!("Guess value must be greater than or equal to 1, got {}.",
                   value);
        } else if value > 100 {
            panic!("Guess value must be less than or equal to 100, got {}.",
                   value);
        }
        Guess {
            value
        }
    }
}
pub fn add2(v: i32) -> i32 {
    v + 2
}
#[cfg(test)]
mod tests { //unit test
    #[test]
    fn it_works() {
        assert_eq!(2 + 2, 4);
    }
    #[test]
    #[ignore] //cargo test -- --ignored
    fn another() {
        panic!("Failed!!!");
    }
    use super::*;
    #[test]
    fn larger_can_hold_smaller() {
        let larger = Rectangle { width: 8, height: 7 };
        let smaller = Rectangle { width: 5, height: 1 };
        assert!(larger.can_hold(&smaller));
    }
    #[test]
    fn smaller_cannot_hold_larger() {
        let larger = Rectangle { width: 8, height: 7 };
        let smaller = Rectangle { width: 5, height: 1 };
        assert!(!smaller.can_hold(&larger));
    }
    #[test]
    fn it_adds_two() {
        assert_eq!(4, 2 + 2);
        assert_ne!(4, 2 + 2);
    }
    #[test]
    fn con() {
        let res = "Hello world.";
        assert!(
            res.contains("word"),
            "not contain",
        );
    }
    #[test]
    #[should_panic(expected = "Guess value must be less than or equal to 100")]
    fn greater_than_100() {
        Guess::new(200);
    }
    #[test]
    fn it_works_() -> Result<(), String> {
        if 2 + 2 == 4 {
            Ok(())
        } else {
            Err(String::from("not equal"))
        }
    }
}
//cargo test it --> 3 test fn it*
//cargo test -- --test-threads=1
//cargo test --help
//cargo test -- --help
//cargo test --test integration_test
//or cargo test (with no mod tests)

//tests/integration_test.rs
use adder;
//tests/common/mod.rs
//mod common;
#[test]
fn it_integrate_test() {
    assert_eq!(4, adder::add2(3));
}

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

//lib.rs
use std::error::Error;
use std::fs;
use std::env;
pub struct Config {
    pub query: String,
    pub filename: String,
    pub case_sensitive: bool,
}
impl Config {
    pub fn new(mut args: std::env::Args) -> Result<Config, &'static str> {
        args.next();
        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 run(config: Config) -> Result<(), Box<dyn Error>> {
    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);
    }
    Ok(())
}
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> {
    let query = query.to_lowercase(); //-> String
    contents.lines()
        .filter(|line| line.to_lowercase().contains(&query))
        .collect()
}
#[cfg(test)]
mod tests {
    use super::*;
    #[test]
    fn case_sensitive() {
        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)
        );
    }
}
//main.rs
use std::env;
use std::process;
use minigrep::Config;
/// Cargo Doc Test
/// # Ahahaha
/// ```
/// let a = |x| x + 1;
/// assert_eq!(1, 2);
/// ```
fn main() {
    let config = Config::new(env::args()).unwrap_or_else(|err| {
        eprintln!("Problem parsing arguments: {}", err);//stderr
        process::exit(1);
    });
    if let Err(e) = minigrep::run(config) {
        eprintln!("Application error: {}", e);
        process::exit(1);
    }
}
tags: blog