RUST学习4

Result<T, E>

Rust doesn’t have exceptions. Instead, it has the type Result<T, E> for recoverable errors and the panic! macro that stops execution when the program encounters an unrecoverable error.

When the panic! macro executes, your program will print a failure message, unwind and clean up the stack, and then quit.

result enum is defined as having two variants, Ok and Err,

The Result<T, E> type has many helper methods defined on it to do various tasks. One of those methods, called unwrap, is a shortcut method that is implemented just like the match

1
2
3
4
5
6
use std::fs::File;

fn main() {
let f = File::open("hello.txt").unwrap();
}

1
2
3
4
5
6
use std::fs::File;

fn main() {
let f = File::open("hello.txt").expect("Failed to open hello.txt");
}

Propagating Errors

1
2
3
4
5
6
7
8
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),
};

A Shortcut for Propagating Errors: the ? Operator
The?placed after a Result value is defined to work in almost the same way as the match expressions

If the value of the Result is an Ok, the value inside the Ok will get returned from this expression, and the program will continue. If the value is an Err, the Err will be returned from the whole function as if we had used the return keyword so the error value gets propagated to the calling code.

1
2
File::open("hello.txt")?.read_to_string(&mut s)?;
Ok(s)
1
2
fn read_username_from_file() -> Result<String, io::Error> {
fs::read_to_string("hello.txt")

If you can ensure by manually inspecting the code that you’ll never have an Err variant, it’s perfectly acceptable to call unwrap. Here’s an example:

Generic 泛型

将代码抽取出来,减少重复代码

1
2
3
4
5
6
7
8
9
10
11
12
fn largest<T>(list: &[T]) -> T {


struct Point<T, U> { //T U代表不同的类型
x: T,
y: U,
}
fn main() {
let both_integer = Point { x: 5, y: 10 };
let both_float = Point { x: 1.0, y: 4.0 };
let integer_and_float = Point { x: 5, y: 4.0 };
}

where T was filled in with the type std::fs::File when the file was opened successfully and E was filled in with the type std::io::Error when there were problems opening the file.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
struct Point<T> {
x: T,
y: T,
}

impl<T> Point<T> {
fn x(&self) -> &T {
&self.x
}
}

fn main() {
let p = Point { x: 5, y: 10 };

println!("p.x = {}", p.x());
}

Note that we have to declare T just after impl so we can use it to specify that we’re implementing methods on the type Point.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
struct Point<T, U> {
x: T,
y: U,
}

impl<T, U> Point<T, U> {
fn mixup<V, W>(self, other: Point<V, W>) -> Point<T, W> {
Point {
x: self.x,
y: other.y,
}
}
}


fn main() {
let p1 = Point { x: 5, y: 10.4 };
let p2 = Point { x: "Hello", y: 'c' };

let p3 = p1.mixup(p2);

println!("p3.x = {}, p3.y = {}", p3.x, p3.y);
}

traits

Different types share the same behavior if we can call the same methods on all of those types. Trait definitions are a way to group method signatures together to define a set of behaviors necessary to accomplish some purpose.

Each type implementing this trait must provideits own custom behaviorfor the body of the method.
相当于提供一个接口,具体内容自己实现
与Java一个概念类似

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
#![allow(unused_variables)]
fn main() {
pub trait Summary {
fn summarize(&self) -> String;
}
}

#![allow(unused_variables)]
fn main() {
pub trait Summary {
fn summarize(&self) -> String;
}

pub struct NewsArticle {
pub headline: String,
pub location: String,
pub author: String,
pub content: String,
}

impl Summary for NewsArticle { //添加trait到结构中
fn summarize(&self) -> String {
format!("{}, by {} ({})", self.headline, self.author, self.location)
}
}

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.username, self.content)
}
}
}

Note that because we defined the Summary trait and the NewsArticle and Tweet types in the same lib.rs in Listing 10-13, they’re all in the same scope. Let’s say this lib.rs is for a crate we’ve called aggregator and someone else wants to use our crate’s functionality to implement the Summary trait on a struct defined within their library’s scope. They would need to bring the trait into their scope first. They would do so by specifying use aggregator::Summary;, which then would enable them to implement Summary for their type. The Summary trait would also need to be a public trait for another crate to implement it, which it is because we put the pub keyword before trait in Listing 10-12.

with trait implementations is that we can implement a trait on a type only if either the trait or the type is local to our crate
can’t implement external traits on external types.

as we implement the trait on a particular type, we can keep or override each method’s default behavior.
specify an empty impl block with impl Summary for NewsArticle {}.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
#![allow(unused_variables)]
fn main() {
pub trait Summary {
fn summarize(&self) -> String {
String::from("(Read more...)")
}
}

pub struct NewsArticle {
pub headline: String,
pub location: String,
pub author: String,
pub content: String,
}

impl Summary for NewsArticle {}

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.username, self.content)
}
}
}

Default implementations can call other methods in the same trait, even if those other methods don’t have a default implementation.

![allow(unused_variables)]
fn main() {
pub trait Summary {
fn summarize_author(&self) -> String;

fn summarize(&self) -> String {
    format!("(Read more from {}...)", self.summarize_author())
}

}

pub struct Tweet {
pub username: String,
pub content: String,
pub reply: bool,
pub retweet: bool,
}

impl Summary for Tweet {
fn summarize_author(&self) -> String {
format!(“@{}”, self.username)
}
}
}

pub trait Summary {
fn summarize(&self) -> String;
}

pub struct NewsArticle {
pub headline: String,
pub location: String,
pub author: String,
pub content: String,
}

impl Summary for NewsArticle {
fn summarize(&self) -> String {
format!(“{}, by {} ({})”, self.headline, self.author, self.location)
}
}

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.username, self.content)
}
}

pub fn notify(item: &impl Summary) {
println!(“Breaking news! {}”, item.summarize());
}

bound trait

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, U>(t: &T, u: &U) -> i32
    where T: Display + Clone,
          U: Clone + Debug
{

an also use the impl Trait syntax in the return position to return a value of some type that implements a trait,
pub trait Summary {
fn summarize(&self) -> String;
}

pub struct NewsArticle {
pub headline: String,
pub location: String,
pub author: String,
pub content: String,
}

impl Summary for NewsArticle {
fn summarize(&self) -> String {
format!(“{}, by {} ({})”, self.headline, self.author, self.location)
}
}

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.username, self.content)
}
}

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,
}
}

1
2
3
4
5
6
7
8
9
10
11
fn largest<T: PartialOrd + Copy>(list: &[T]) -> T { //限制只能传进i32 char等
let mut largest = list[0];

for &item in list {
if item > largest {
largest = item;
}
}

largest
}

fn main() {
let number_list = vec![34, 50, 25, 100, 65];

let result = largest(&number_list);
println!("The largest number is {}", result);

let char_list = vec!['y', 'm', 'a', 'q'];

let result = largest(&char_list);
println!("The largest char is {}", result);

}

impl<T: Display> ToString for T {
// –snip–
}

The Borrow Checker

we’ll add generic lifetime parameters that define the relationship between the references

lifetime
Lifetime annotations don’t change how long any of the references live.

&i32        // a reference
&'a i32     // a reference with an explicit lifetime
&'a mut i32 // a mutable reference with an explicit lifetime

have a function with the parameter first that is a reference to an i32 with lifetime ‘a. The function also has another parameter named second that is another reference to an i32 that also has the lifetime ‘a. The lifetime annotations indicate that the references first and second must both live as long as that generic lifetime.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
fn main() {
let string1 = String::from("abcd");
let string2 = "xyz";

let result = longest(string1.as_str(), string2);
println!("The longest string is {}", result);
}

fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {
if x.len() > y.len() {
x
} else {
y
}
}

返回的引用取最短的lifetime

1
2
3
4
5
6
7
8
9
10
11
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,
};
}

an instance of ImportantExcerpt can’t outlive the reference it
holds in its part field.

Lifetime Elision

由编译器进行引用lifetime的编写
三个阶段

The Static Lifetime

1
2
3
4
#![allow(unused_variables)]
fn main() {
let s: &'static str = "I have a static lifetime.";
}

means that this reference can live for the entire duration of the program

lifetimes are a type of generic,
fn main() {
let string1 = String::from(“abcd”);
let string2 = “xyz”;

let result = longest_with_an_announcement(
    string1.as_str(),
    string2,
    "Today is someone's birthday!",
);
println!("The longest string is {}", result);

}

use std::fmt::Display;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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
}
}

test

cargo test
Checking Results with the assert! Macro
The assert! macro, provided by the standard library, is useful when you want to ensure that some condition in a test evaluates to true.

assert_eq! and assert_ne!These macros compare two arguments for equality or inequality, respectively.

should_panic 产生panic返回正确

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
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 }
}
}

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

#[test]
#[should_panic]
fn greater_than_100() {
Guess::new(200);
}
}

fn main() {}

#[test]
#[should_panic(expected = "Guess value must be less than or equal to 100")]

the unit type, ()

TDD process

test 还是很不多的,C++的时候都一遍遍输入参数

$ cargo run > output.txt