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 | use std::fs::File; |
1 | use std::fs::File; |
Propagating Errors
1 | fn read_username_from_file() -> Result<String, io::Error> { |
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 | File::open("hello.txt")?.read_to_string(&mut s)?; |
1 | fn read_username_from_file() -> Result<String, io::Error> { |
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 | fn largest<T>(list: &[T]) -> T { |
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 | struct Point<T> { |
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 | struct Point<T, U> { |
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 behavior
for the body of the method.相当于提供一个接口,具体内容自己实现
与Java一个概念类似
1 | #![allow(unused_variables)] |
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 | #![allow(unused_variables)] |
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 | fn largest<T: PartialOrd + Copy>(list: &[T]) -> T { //限制只能传进i32 char等 |
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 | fn main() { |
返回的引用取最短的lifetime
1 | struct ImportantExcerpt<'a> { |
an instance of ImportantExcerpt can’t outlive the reference it
holds in its part field.
Lifetime Elision
由编译器进行引用lifetime的编写
三个阶段
The Static Lifetime
1 | #![allow(unused_variables)] |
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 | fn longest_with_an_announcement<'a, T>( |
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 | pub struct Guess { |
the unit type, ()
TDD process
test 还是很不多的,C++的时候都一遍遍输入参数
$ cargo run > output.txt