Generic Types, Traits, and Lifetimes
Removing Duplication by Extracting a Function
如果代码需要重复使用,考虑封装成函数
Generic Data Types
泛型:提高代码的复用性
函数泛型格式
fn fn_name<T,...>(paraments...) -> type {
}
可以看到只需要加<T…> 即可
fn max_element<T>(item: &[T]) -> T {
let mut val = item[0];
for &x in item {
if x > val {
val = x;
}
}
return val;
}
// binary operation `>` cannot be applied to type `T
// 这个暂时不管
结构体泛型
格式
struct name<T..> {
name1: T,
...
}
struct Point<T> {
x: T,
y: T,
}
枚举中的泛型
Option<T>
,Result<T, E>
方法泛型
impl<T> Point<T> {// T 要 impl<T>
fn x(&self) -> &T {
&self.x
}
}
impl Point<i32> { // 具体类型 无 <T>
fn y(&self) -> &i32 {
&self.y
}
}
struct Point<T, U> {
x: T,
y: U,
}
impl<T, U> Point<T, U> {
fn mixed<V, W>(self, p: Point<V, W>) -> Point<T, W> {
Point {
x: self.x,
y: p.y,
}
}
}
Traits: Defining Shared Behavior
定义trait,使用关键字 trait
pub trait Summary {
fn summary(&self) -> String;
}
只有函数签名,无具体实现,也可以默认实现
实现 trait
格式
impl trait_name for struct_name {
...
}
// lib
pub trait Summary {
fn summary(&self) -> String;
}
pub struct Data {
pub username: String,
}
impl Summary for Data {
fn summary(&self) -> String {
format!("{}", self.username)
}
}
// main
use rust::Data;// 引入 //[package] name = "rust"
use rust::Summary;
fn main() {
let x = Data {
username: String::from("SCUT"),
};
println!("{}", x.summary());
}
实现 trait 的约束
这个 类型 或 这个 trait 是本地 crate 里定义的
使用 trait 作为参数
impl trait
语法,适用于简单情况
fn test(x: impl Summary) {
}
泛型写法
fn test<T: Summary>(x: T) {
}
+
添加多个 trait
fn test<T: Summary + Display>(x: T) {
}
如果有多个量需要不同 trait
fn test<T: Summary + Display, U: Clone>(x: T, y: U) {
}
where
语句
fn test<T, U>(x: T, y: U) -> () where
T: Summary + Display,
U: Clone,
{}
trait 作为返回类型, 只能返回同一种类型
fn test() -> impl Summary {
Data {
username: String::from("SCUT"),
}
}
Validating References with Lifetimes
看一个例子
fn main() {
let mut r;
{
let x = 5;
r = &x;
}
println!("{}", r);
}
// borrowed value does not live long enough
x 的生命周期小于 r,引用的对象生命周期必须大于等于引用者
生命周期参数名:以 ’
开头,通常全小写,一般用 ’a
生命周期标注位置:在引用的 &
后面,使用空格将标注和引用类型分开
&i32 // 引用
& 'a i32 // 带有显示生命周期的引用
& 'a mut i32 // 带有显示生命周期的可变引用
例如
fn longest(str1: &String, str2: &String) -> &String {
if str1.len() > str2.len() {
str1
} else {
str2
}
}
fn main() {
let s = String::from("0");
let t = String::from("2024");
println!("{}", longest(&s, &t));
}
// expected named lifetime parameter
无法判断 str1 和 str2 的生命周期长度
更改为
fn longest<'a>(str1: &'a String, str2: &'a String) -> &'a String {
if str1.len() > str2.len() {
str1
} else {
str2
}
}
fn main() {
let s = String::from("0");
let t = String::from("2024");
println!("{}", longest(&s, &t));
}
这样生命周期会取较小的那个,实际的生命周期未改变
结构体与生命周期
struct Ie<'a> {
part: &'a str,
}
fn main() {
let s = "2024";
let i = Ie {
part: s,
};
}
// 字段的生命周期要大于等于对象本身的生命周期
生命周期的省略规则
所有引用都有生命周期,需要为使用生命周期的函数或 struct 指定生命周期参数
如果应用规则后,引用的生命周期仍模糊不清 → 编译错误,解决办法:添加生命周期标注,表明引用间的相互关系
输入、输出生命周期
函数/方法的参数:输入生命周期
函数/方法的返回值:输出生命周期
三个规则
- 每个引用类型的参数都有自己的生命周期
- 如果只有一个输入生命周期参数,那么该生命周期会被赋给所有输出生命周期
- 如果有多个输入生命周期,但其中一个是 &self 或 &mut self (方法),那么 self 的生命周期会被赋给所有输出生命周期参数、
在应用三个规则后,输出生命周期还不清楚,编译错误
方法中的生命周期标注
和泛型类似
struct Ie<'a> {
part: &'a str,
}
impl<'a> Ie<'a> {
fn lv(&self) -> i32 {
0
}
fn rt(&self, s: & str) ->&str {
self.part
}// 应用规则三
}
静态生命周期
'static
所有字符串字面值都拥有 static 生命周期
let s:&'static str = "2024";
生命周期与 trait bound
fn test<'a, T>(s: &'a str, t: &'a str, z: T) -> &'a str
where
T: Display,
{
println!("{}", z);
if s.len() < t.len() {
t
} else {
s
}
}