Option
(Some
, None
)
match
, if let
, "ръкави")
ref
, ref mut
)enum Act {
TooAwesome { name: String, reason: String },
Awesome(String),
Lame,
}
fn pass_the_act(_: &Act) { /*...*/ }
fn main() {
let act = Act::Lame;
match act {
Act::TooAwesome { name, ... } => println!("{} е върхът!", name),
Act::Awesome { name } => println!("{} е супер!", name),
Act::Lame => println!("Кофти.."),
}
pass_the_act(&act);
}
enum Act {
TooAwesome { name: String, reason: String },
Awesome(String),
Lame,
}
fn pass_the_act(_: &Act) { /*...*/ }
fn main() {
let act = Act::Lame;
match act {
Act::TooAwesome { ref name, .. } => println!("{} е върхът!", name),
Act::Awesome(ref name) => println!("{} е супер!", name),
Act::Lame => println!("Кофти.."),
}
pass_the_act(&act);
}
Вече сме ги виждали
Option<T>
Vec<T>
Със знанията събрани до сега
fn add_i32(a: i32, b: i32) -> i32 {
a + b
}
fn add_u8(a: u8, b: u8) -> u8 {
a + b
}
С обобщени типове
fn add<T>(a: T, b: T) -> T {
a + b
}
Получаваме
error[E0369]: binary operation `+` cannot be applied to type `T` | 2 | a + b | ^^^^^ | = note: an implementation of `std::ops::Add` might be missing for `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 };
}
А какво ще стане, ако опитаме да я създадем по този начин?
fn main() {
let what_about_this = Point { x: 5, y: 4.0 }; // ??
}
error[E0308]: mismatched types | 9 | let what_about_this = Point { x: 5, y: 4.0 }; | ^^^ expected integral variable, found floating-point variable | = note: expected type `{integer}` found type `{float}`
Ако искаме двете координати да могат да са различни типове
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 };
}
Миналия път разгледахме какво прави Option
Това е дефиницията му:
enum Option<T> {
Some(T),
None,
}
enum Message<T, A> {
Text(T),
Action(A),
}
struct Point<T> { x: T, y: T }
// Забележете impl<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); // ??
println!("p.x() = {}", p.x()); // ??
}
Стандартен изход на примера
p.x = 5 p.x() = 5
За любознателните - има начин как да се вземе адрес към property-то чрез абсолютен път
println!("p.x = {:x}", Point::<u32>::x as usize);
println!("p.x = {:x}", Point::<f64>::x as usize);
Turbofish! → ::<>
В този пример само Point<f32>
ще притежава този метод
// Този път няма impl<T>
impl Point<f32> {
fn distance_from_origin(&self) -> f32 {
(self.x.powi(2) + self.y.powi(2)).sqrt()
}
}
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);
}
fn to_json<T>(val: T) -> String {
...
}
{:?}
, когато отпечатваме на екрана?
Debug
.
Нека си дефинираме trait ToJSON:
trait ToJSON {
fn to_json(&self) -> String;
}
Сега можем да го имплементираме за някои вградени типове данни:
impl ToJSON for String {
fn to_json(&self) -> String {
format!("\"{}\"", self)
}
}
Сега можем да го имплементираме за някои вградени типове данни:
impl ToJSON for i32 {
fn to_json(&self) -> String {
format!("{}", self)
}
}
impl ToJSON for f32 {
fn to_json(&self) -> String {
format!("{}", self)
}
}
println!("String as json: {}", String::from("mamal").to_json());
// String as json: "mamal"
println!("Number as json: {}", 3.to_json());
// Number as json: 3
Между другото, можем да имаме имплементация по подразбиране:
trait ToJSON {
fn to_json(&self) -> String {
String::from("null")
}
}
impl ToJSON for () {}
println!("Unit as json: {}", ().to_json());
// Unit as json: null
Още малко - за Option!
impl <T> ToJSON for Option<T> where T : ToJSON {
fn to_json(&self) -> String {
match self {
&Some(ref val) => val.to_json(),
&None => String::from("null")
}
}
}
В JSON има списъци, нека да пробваме да го направим за вектор:
impl <T> ToJSON for Vec<T> where T: ToJSON {
fn to_json(&self) -> String {
let mut result: String = String::from("");
for e in self.iter() {
result.push_str(&e.to_json());
result.push_str(",");
}
result.pop();
format!("[{}]", result)
}
}
В JSON има списъци, нека да пробваме да го направим за вектор:
println!("Vector as json: {}", vec![Some(1.1), Some(2.2), None].to_json());
// Vector as json: [1.1, 2.2, null]
А сега и за наш си тип:
struct Student {
age: i32,
full_name: String,
number: i32,
hobby: Option<String>
}
impl ToJSON for Student {
fn to_json(&self) -> String {
format!(
r#"
{{
"age": {},
"fullName": {},
"number": {},
"hobby": {}
}}"#,
self.age.to_json(), self.full_name.to_json(),
self.number.to_json(), self.hobby.to_json()
)
}
}
Сега можем да си дефинираме функцията, от която почна всичко:
fn to_json<T: ToJSON>(value: T) -> String {
value.to_json()
}
А ако искаме дадена стойност да имплементира повече от един trait?
fn log_json_transformation<T: ToJSON + Debug>(value: T) {
println!("{:?} -> {}", value, value.to_json());
}
Можем да имплементираме trait T
за тип S
ако:
T
е дефиниран в нашия crate, или
S
е дефиниран в нашия crateИма начин да се използва една версия на функцията и runtime тя да се избира.
Това става с trait обекти - ако имаме trait Stuff
, &Stuff
да представлява какъвто и да е обект имплементиращ trait-а.
fn to_json(value: &ToJSON) -> String {
value.to_json()
}
let trait_object: &ToJSON = &5;
println!("{}", to_json(trait_object));
to_json(&5);
to_json(&5 as &ToJSON);
Можем да използваме trait обекти да си направим не-хомогенен вектор, който може да се принтира.
use std::fmt::Debug;
println!("{:?}", vec![
&1.1 as &Debug,
&Some(String::from("Stuff")),
&3
]);
Нека си дефинираме trait за събиране:
trait Add<RHS=Self> {
type Output;
fn add(self, rhs: RHS) -> Self::Output;
}
impl Add for i32 {
type Output = i32;
fn add(self, rhs: i32) -> i32 {
self + rhs
}
}
impl Add for String {
type Output = String;
fn add(self, rhs: String) -> String {
format!("{} {}", self, rhs)
}
}
Асоциираният тип ни дава повече възможности.
Да речем резултата от събирането, може да е друг тип:
struct StudentGroup {
members: Vec<Student>
}
impl Add for Student {
type Output = StudentGroup;
fn add(self, rhs: Student) -> StudentGroup {
StudentGroup { members: vec![self, rhs] }
}
}