泛型
使用<T>来表示使用泛型,这块和golang相差不大
struct Point<X1, Y1> {
x: X1,
y: Y1,
}
impl<X1, Y1> Point<X1, Y1> {
fn mixup<X2, Y2>(self, other: Point<X2, Y2>) -> Point<X1, Y2> {
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 特征
这个类似其他语言中的接口(interface)。比较特别的地方是可以在这个特征中实现具体的函数,简化这个特征的实现。
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)
}
}
函数入参可以直接指定特征
pub fn notify(item: &impl Summary) {
println!("Breaking news! {}", item.summarize());
}
另外一种更加省力的写法(称为a trait bound)
pub fn notify<T: Summary>(item: &T) {
println!("Breaking news! {}", item.summarize());
}
如果一个入参需要有多个特征,使用+来指定
// pub fn notify(item: &(impl Summary + Display)) {
pub fn notify<T: Summary + Display>(item: &T) {
生命周期
这块稍微看的有点晕。简单说就是有时候写的代码会存在一些回收的歧义,需要指定生命周期来让编译器能够编译通过。
// 这里x、y都有可能返回,编译器无法进行判断(什么时候进行回收)
fn longest(x: &str, y: &str) -> &str {
if x.len() > y.len() {
x
} else {
y
}
}
为了编译通过,需要指定生命周期,一般用'a来指定
// x、y、返回值有相同的生命周期
fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {
if x.len() > y.len() {
x
} else {
y
}
}
使用'static来指定整个程序生命周期
let s: &'static str = "I have a static lifetime.";
汇总
把上述三个特性都整合在一起,一个函数会像下面这个样子(有点酷):
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
}
}