泛型 Generic Type
如下两个function,实现从slice 切片中找到最大的值并返回,分别为i32类型,char类型分别定义了两个function
fn largest_i32(list: &[i32]) -> i32 {
let mut largest = list[0];
for &item in list {
if item > largest {
largest = item;
}
}
largest
}
fn largest_char(list: &[char]) -> char {
let mut largest = list[0];
for &item in list {
if item > largest {
largest = item;
}
}
largest
}
代码重复太高了,改成Rust支持的泛型格式
fn largest<T>(list: &[T]) -> T {
let mut largest = list[0];
for &item in list {
if item > largest {
largest = item;
}
}
largest
}
此处的T可以是i32等类型,但是要注意在silce的iteration中,有个比较大小的符号“>”,该运算符是否在T类中中被支持
如果不支持,则会看到如下报错
$ cargo run
Compiling chapter10 v0.1.0 (file:///projects/chapter10)
error[E0369]: binary operation `>` cannot be applied to type `T`
--> src/main.rs:5:17
|
5 | if item > largest {
| ---- ^ ------- T
| |
| T
|
help: consider restricting type parameter `T`
|
1 | fn largest<T: std::cmp::PartialOrd>(list: &[T]) -> T {
| ^^^^^^^^^^^^^^^^^^^^^^
For more information about this error, try `rustc --explain E0369`.
error: could not compile `chapter10` due to previous error
需要改成使T满足约束PartialOrd,也就是可以部分排序
fn largest<T:PartialOrd>(list: &[T]) -> T {
let mut largest = list[0];
for &item in list {
if item > largest {
largest = item;
}
}
largest
}
Traits 特性
Traits,英文翻译为“特性”,从功能上相当于Java的interface
pub trait Summary {
fn summarize(&self) -> String;
}
举栗子:
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)
}
}
对于Tweet和NewsArticle来说方法 summarize是有不同的实现的
下面notify方法的参数,支持实现了Summary Trait的具体的类型
pub fn notify(item: &impl Summary) {
println!("Breaking news! {}", item.summarize());
}
Trait Bound: T: Summary 约束
pub fn notify<T: Summary>(item: &T) {
println!("Breaking news! {}", item.summarize());
}
pub fn notify(item1: &impl Summary, item2: &impl Summary) {} //item1和item2可能是不同的类型
pub fn notify<T: Summary>(item1: &T, item2: &T) {} //同一种类型
Mutiple Trait Bound:多重约束
pub fn notify(item: &(impl Summary + Display)) {}
pub fn notify<T: Summary + Display>(item: &T) {}
在约束中使用where
fn some_function<T: Display + Clone, U: Clone + Debug>(t: &T, u: &U) -> i32 {
fn some_function<T, U>(t: &T, u: &U) -> i32
where T: Display + Clone,
U: Clone + Debug
{}
函数返回值可以使用Trait吗?看如下例子
fn returns_summarizable(switch: bool) -> impl Summary {
if switch {
NewsArticle {
headline: String::from(
"Penguins win the Stanley Cup Championship!",
),
location: String::from("Pittsburgh, PA, USA"),
author: String::from("Iceburgh"),
content: String::from(
"The Pittsburgh Penguins once again are the best \
hockey team in the NHL.",
),
}
} else {
Tweet {
username: String::from("horse_ebooks"),
content: String::from(
"of course, as you probably already know, people",
),
reply: false,
retweet: false,
}
}
}
Rust编译失败,不允许返回 impl Summary;
解决办法是使用Box<dyn Summary>
fn largest<T: PartialOrd>(list: &[T]) -> T {} //编译失败!
fn largest<T: PartialOrd + Copy>(list: &[T]) -> T {
let mut largest = list[0];
for &item in list {
if item > largest {
largest = item;
}
}
largest
}
改进largest方法,返回&T
使用Trait Bound有条件的实现方法:
struct Pair<T> {
x: T,
y: T,
}
impl<T> Pair<T> {
fn new(x: T, y: T) -> Self {
Self { x, y }
}
}
impl<T: Display + PartialOrd> Pair<T> {
fn cmp_display(&self) {
if self.x >= self.y {
println!("The largest member is x = {}", self.x);
} else {
println!("The largest member is y = {}", self.y);
}
}
}
此处的cmp_display方法只对支持Display和PartialOrd的类型有效
对所有支持Display Trait的类型实现ToString Trait,从而可以调用to_string()方法
impl<T: Display> ToString for T {
// --snip--
}
let s = 3.to_string(); //调用正常
lifetime生命周期
Rust中的引用都有生命周期,代表引用有效的范围,大部分时候,引用是隐式的,可被推导出来;如果类型有可能被推导为多种可能性,必须要显示的声明,从而保证运行时是可用的引用。
防止使用悬挂引用Dangling reference
fn main() {
{
let r;
{
let x = 5;
r = &x;
}
println!("r: {}", r);
}
}
r 变量被赋值为x的引用,当试图打印r的时候,x变量已经超出了scope而被销毁,所有会有如下报错,提示x生命周期不够长
$ cargo run
Compiling chapter10 v0.1.0 (file:///projects/chapter10)
error[E0597]: `x` does not live long enough
--> src/main.rs:7:17
|
7 | r = &x;
| ^^ borrowed value does not live long enough
8 | }
| - `x` dropped here while still borrowed
9 |
10 | println!("r: {}", r);
| - borrow later used here
For more information about this error, try `rustc --explain E0597`.
error: could not compile `chapter10` due to previous error
借用检查
Rust的借用检查borrow checker会检查所有范围确保借用是有效的
fn main() {
{
let r; // ---------+-- 'a
// |
{ // |
let x = 5; // -+-- 'b |
r = &x; // | |
} // -+ |
// |
println!("r: {}", r); // |
} // ---------+
}
上述代码中,r的生命周期范围是a,x的生命周期范围是
b,明显a比
b范围更大,r引用了一个变量x,而x的生命周期b并不足够长,也就是r会指向一个不可用的引用,所以Rust会报错提示
x` does not live long enough
改成如下即可
fn main() {
{
let x = 5; // ----------+-- 'b
// |
let r = &x; // --+-- 'a |
// | |
println!("r: {}", r); // | |
// --+ |
} // ----------+
}
修改后,x的生命周期b比
a范围更大,保证了r指向的引用x会一直有效
函数中的生命周期
如下函数,返回更长的那个字符串
fn longest(x: &str, y: &str) -> &str {
if x.len() > y.len() {
x
} else {
y
}
}
编译不能通过,报错如下:缺少生命周期的定义
$ cargo run
Compiling chapter10 v0.1.0 (file:///projects/chapter10)
error[E0106]: missing lifetime specifier
--> src/main.rs:9:33
|
9 | fn longest(x: &str, y: &str) -> &str {
| ---- ---- ^ expected named 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`
help: consider introducing a named lifetime parameter
|
9 | fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {
| ^^^^ ^^^^^^^ ^^^^^^^ ^^^
For more information about this error, try `rustc --explain E0106`.
error: could not compile `chapter10` due to previous error
生命周期注解语法
&i32 // a reference
&'a i32 // a reference with an explicit lifetime
&'a mut i32 // a mutable reference with an explicit lifetime
下面的代码,对参数x,y都定义在生命周期`a,所以无论返回x还是y,都是可见的,可以编译
fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {
if x.len() > y.len() {
x
} else {
y
}
}
总结:生命周期的意义在于对入参和返回值的生命周期进行协调,确保他们符合规则,否则编译器报错,同时Rust编译器也有了足够的信息来保证内存的安全,避免悬挂指针,野指针等一些内存问题。
struct中的生命周期
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(s: &str) -> &str {
let bytes = s.as_bytes();
for (i, &item) in bytes.iter().enumerate() {
if item == b' ' {
return &s[0..i];
}
}
&s[..]
}
上面的函数没有生命周期参数,也能编译,原因是Rust编译器可以自行推导出来生命周期,其实相当于
fn first_word<'a>(s: &'a str) -> &'a str {}
函数或方法的参数生命周期,称为输入生命周期;
返回值的生命周期,称为输出生命周期;
规则:
- 规则1:每个引用的入参,都有自己的生命周期
- 规则2:如果只有一个输入生命周期参数,那么输出生命周期和输入相同
- 规则3:入参中有&self,&mut self,则把self的生命周期赋给返回值
方法的生命周期
impl<'a> ImportantExcerpt<'a> {
fn level(&self) -> i32 {
3
}
}
impl<'a> ImportantExcerpt<'a> {
fn announce_and_return_part(&self, announcement: &str) -> &str {
println!("Attention please: {}", announcement);
self.part
}
}
静态生命周期
在程序的整个生命周期中一直有效
let s: &'static str = "I have a static lifetime.";
完整的例子
融合了traits bound,lifetime,generic的例子
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
}
}
参考:
1) https://blog.csdn.net/setlilei/article/details/120717780 比较eq, partialeq, Ord, PartialOrd
网友评论