Option<T>
Vec<T>
HashMap<K, V>
duplicated functions
fn largest_i32(list: &[i32]) -> i32 {}
fn largest_char(list: &[char]) -> char {}
how about
fn largest<T>(list: &[T]) -> T {}
fn largest<T>(list: &[T]) -> T {}
struct Point<T> {
x: T,
y: T,
}
fn main() {
let integer = Point { x: 5, y: 10 };
let float = Point { x: 1.0, y: 4.0 };
}
Two generics in a Struct
is fine
struct Point<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 };
}
enum Option<T> {
Some(T),
None,
}
enum Result<T, E> {
Ok(T),
Err(E),
}
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());
}
Not work. why?
impl Point<T> {
fn x(&self) -> &T {
&self.x
}
}
need bring scope in impl
T
We don't add type after impl
if we don't need generics from Struct
impl Point<f32> {
fn distance_from_origin(&self) -> f32 {
(self.x.powi(2) + self.y.powi(2)).sqrt()
}
}
Other types in impl
different from types of Struct
are allowed.
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);
}
Monomorphization 單態化
let integer = Some(5);
let float = Some(5.0);
turn out to be
enum Option_i32 {
Some(i32),
None,
}
enum Option_f64 {
Some(f64),
None,
}
fn main() {
let integer = Option_i32::Some(5);
let float = Option_f64::Some(5.0);
}
trait
keywordpub trait Summary {
fn summarize(&self) -> String;
}
pub struct NewsArticle {
pub headline: String,
// ...
}
impl Summary for NewsArticle {
fn summarize(&self) -> String {
format!("{}, by {} ({})", self.headline, self.author, self.location)
}
}
pub struct Tweet {
pub username: String,
// ...
}
impl Summary for Tweet {
fn summarize(&self) -> String {
format!("{}: {}", self.username, self.content)
}
}
Call the method just as regular methods, such as tweet.summarize()
let tweet = Tweet {
username: String::from("tigercosmos"),
// ...
};
println!("1 new tweet: {}", tweet.summarize());
pub
if it would be used otherwheretraits could be used for local types. external traits on external types is forbidden.
Popular traits: Display, Debug, ToString, Default, should be implemented in a crate, becasue others cannot implement theirselves.
<T as TraitName>::item
trait Cook {
fn start(&self);
}
trait Wash {
fn start(&start);
}
struct Chef;
impl Cook for Chef {
fn start(&seld) { println!("Cook Start"); }
}
impl Wash for Chef {
fn start(&seld) { println!("Cook Start"); }
}
fn main {
let me = Chef;
<Cook>::start(&me);
<Chef as Wash>::start(&me);
}
define the method when declare the trait
pub trait Summary {
fn summarize(&self) -> String {
String::from("(Read more...)")
}
}
let article = NewsArticle {
// .....
};
println!("New article available! {}", article.summarize());
// New article available! (Read more...)
trait
could call other methods in the same trait or the struct member
pub struct Tweet {
pub username: String,
pub content: String,
pub reply: bool,
pub retweet: bool,
}
pub trait Summary {
fn summarize_author(&self) -> String;
fn summarize(&self) -> String {
format!("(Read more from {}...)", self.summarize_author())
}
}
impl Summary for Tweet {
fn summarize_author(&self) -> String {
format!("@{}", self.username)
}
}
fn main() {
let tweet = Tweet {
username: String::from("tigercosmos"),
content: String::from("You should follow @tigercosmos's github :)"),
reply: false,
retweet: false,
};
println!("1 new tweet: {}", tweet.summarize());
}
try yourself: playground link
specify that item
must be of a type that implements the Summary
trait:
pub fn notify<T: Summary>(item: T) {
println!("Breaking news! {}", item.summarize());
}
multiple trait bounds on a generic type using the +
syntax
fn some_function<
T: Display + Clone,
U: Clone + Debug
> (t: T, u: U) -> i32 { }
or using where
fn some_function<T, U>(t: T, u: U) -> i32
where T: Display + Clone,
U: Clone + Debug
{ }
an attribute for automatically adding impl
of trait
for Struct
#[derive(Copy, Clone, Default, Debug)]
Struct Foo { }
impl Copy for Foo { /*...*/ };
impl Clone for Foo { /*...*/ };
impl Default for Foo { /*...*/ };
impl Debug for Foo { /*...*/ };
pub trait Service {
type Request;
type Response;
type Error;
};
trait HttpService = Service<
Request = http::Request,
Reponse = http::Response,
Error = http::Error
>;
impl
could be many blocks.
Generics and trait bounds make sense for many block of implement method.
impl<T, U> Point<T, U> {}
impl<T> Point<T, U> {}
impl Point<T, U> {}
impl<T: Display + PartialOrd> Point<T> {}
conditionally implement a trait for any type that implements another trait.
impl<T: Display> ToString for T {
// --snip--
}
{}
{:?}
{:#?}
Self
PartialOrd/Ord/PartialEq/Eq
For a, b, c in a set X
1. Antisymmetry: if a < b, !(a>b);
if a > b, then !(a < b)
2. Transitivity: a > b, b > c, then a > c
3. Completeness: all elements in X must be one of
relationships a > b, a < b, or a == b
partail order: fit 1, 2
total order: fit 1, 2, 3
let nan = std::f32:NAN;
let x = 1.9f32;
println("{}", nan < x); // false
println("{}", nan > x); // false
println("{}", nan == x); // false
// IEEE754
I want to have a generic function that finds the largest one in list
fn largest<T>(list: &[T]) -> T {
let mut largest = list[0];
for &item in list.iter() {
if item > largest {
largest = item;
}
}
largest
}
fn main() {
let number_list = vec![34, 50, 25, 100, 65];
println!("The largest number is {}", largest(&number_list));
let char_list = vec!['y', 'm', 'a', 'q'];
println!("The largest char is {}", largest(&char_list));
}
note: `T` might need a bound for `std::cmp::PartialOrd`
Add <T: PartialOrd + Copy>
here
fn largest<T: PartialOrd + Copy>(list: &[T]) -> T {
let mut largest = list[0];
for &item in list.iter() {
if item > largest {
largest = item;
}
}
largest
}
try yourself: link
PartialOrd
?use std::cmp::Ordering;
#[derive(Eq)]
struct Person { /* fields */}
impl PartialOrd for Person {
fn partial_cmp(&self, other: &Person) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl Ord for Person {
fn cmp(&self, other: &Person) -> Ordering {
self.height.cmp(&other.height)
}
}
impl PartialEq for Person {
fn eq(&self, other: &Person) -> bool {
self.height == other.height
}
}
try yourself: link
Rust checks right away
C++ check while instantiating
{
let r;
{
let x = 5;
r = &x;
}
println!("r: {}", r);
}
--> src/main.rs:7:5
|
6 | r = &x;
| - borrow occurs here
7 | }
| ^ `x` dropped here while still borrowed
...
10 | }
| - borrowed value needs to live until here
{
let r; // ---------+-- 'a
// |
{ // |
let x = 5; // -+-- 'b |
r = &x; // | |
} // -+ |
// |
println!("r: {}", r); // |
} // ---------+
The correct one
{
let x = 5; // ----------+-- 'b
// |
let r = &x; // --+-- 'a |
// | |
println!("r: {}", r); // | |
// --+ |
} // ----------+
fn longest(x: &str, y: &str) -> &str {
if x.len() > y.len() {
x
} else {
y
}
}
--> src/main.rs:1:33
|
1 | fn longest(x: &str, y: &str) -> &str {
| ^ expected lifetime parameter
|
= help: this function's return type contains a borrowed value, but the
signature does not say whether it is borrowed from `x` or `y`
&i32 // a reference
&'a i32 // a reference with an explicit lifetime
&'a mut i32 // a mutable reference with an explicit lifetime
annotations are meant to tell Rust how generic lifetime parameters of multiple references relate to each other.
A fixup for previous one
fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {
if x.len() > y.len() {
x
} else {
y
}
}
We don't need to declare lifetime if it has not "relationship to x" or "return value"
fn longest<'a>(x: &'a str, y: &str) -> &'a str {
x
}
the lifetime parameter for the return type needs to match the lifetime parameter for one of the parameters
fn longest<'a>(x: &str, y: &str) -> &'a str {
let result = String::from("really long string");
result.as_str()
}
3 | result.as_str()
| ^^^^^^ does not live long enough
4 | }
| - borrowed value only lives until here
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 };
}
fn first_word<'a>(s: &'a str) -> &'a str { }
no need lifetime specification here
fn foo<'a, 'b>(x: &'a i32, y: &'b i32)
fn foo<'a>(x: &'a i32) -> &'a i32
// origin
fn first_word(s: &str) -> &str { }
// first rule
fn first_word<'a>(s: &'a str) -> &str { }
// second rule
fn first_word<'a>(s: &'a str) -> &'a str { }
fn foo<'a>(s: &'a str) -> &'a str { }
'static
means the life is the entire duration of the program
fn foo<'a>() -> &'a str {
"I have a static lifetime."
}
fn main() {
println!("{}", foo());
}
fn foo() -> &'static str {
"I have a static lifetime."
}
fn main() {
println!("{}", foo());
}
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
}
}