3.5 Rust Generic Types, Traits, and Lifetimes
Every programming language has tools for effectively handling the duplication of concepts. In Rust, one such tool is generics. Generics are abstract stand-ins for concrete types or other properties.
泛型的作用:降低代码冗余
- Identify duplicate code.
- Extract the duplicate code into the body of the function and specify the inputs and return values of that code in the function signature.
- Update the two instances of duplicated code to call the function instead.
Traits: Defining Shared Behavior
A trait tells the Rust compiler about functionality a particular type has and can share with other types. We can use traits to define shared behavior in an abstract way. We can use trait bounds to specify that a generic can be any type that has certain behavior.
Note: Traits are similar to a feature often called interfaces in other languages, although with some differences.
#![allow(unused)] pub trait Person {
fn food(&self) -> String; fn eat(&self) -> String {
format!("(eat {}...)", self.food())
}
} pub struct Teacher {
pub name: String,
} impl Person for Teacher {
fn food(&self) -> String {
format!("{}", "面包")
}
} pub struct Student {
pub username: String,
pub age: i8,
} impl Person for Student {
fn food(&self) -> String {
format!("{}", "水果")
}
} pub fn test(){
let tch = Teacher {
name: String::from("a01"),
}; println!("teacher: {}", tch.eat()); let st = Student {
username: String::from("Penguins win the Stanley Cup Championship!"),
age: 12,
}; println!("student: {}", st.eat());
}
mod tra; fn main() {
println!("----------------");
tra::tra1::test();
}
输出
----------------
teacher: (eat 面包...)
student: (eat 水果...)
Traits as Parameters
Now that you know how to define and implement traits, we can explore how to use traits to define functions that accept many different types.
pub fn notify_eat(item: &impl Person) {
println!("notify: {}", item.eat());
} pub fn test2(){
let tch = Teacher {
name: String::from("a01"),
}; notify_eat(&tch); let st = Student {
username: String::from("Penguins win the Stanley Cup Championship!"),
age: 12,
}; notify_eat(&st);
}
----------------
notify: (eat 面包...)
notify: (eat 水果...)
Trait Bound Syntax
pub fn notify<T: Person>(item: &T) {
println!("notify: {}", item.eat());
} pub fn test3(){
let tch = Teacher {
name: String::from("a01"),
}; notify(&tch); let st = Student {
username: String::from("hong yun"),
age: 12,
}; notify(&st);
}
----------------
notify: (eat 面包...)
notify: (eat 水果...)
Returning Types that Implement Traits
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)
}
} fn returns_summarizable() -> impl Summary {
Tweet {
username: String::from("horse_ebooks"),
content: String::from(
"of course, as you probably already know, people",
),
reply: false,
retweet: false,
}
}
泛型的复制与比较
#![allow(unused)] fn largest<T: PartialOrd + Copy>(list: &[T]) -> T {
let mut largest = list[0]; for &item in list {
if item > largest {
largest = item;
}
} largest
} pub fn test() {
let number_list = vec![34, 50, 25, 100, 65]; let result = largest(&number_list);
println!("The largest number is {}", result); let char_list = vec!['y', 'm', 'a', 'q']; let result = largest(&char_list);
println!("The largest char is {}", result);
}
If we don’t want to restrict the largest
function to the types that implement the Copy
trait, we could specify that T
has the trait bound Clone
instead of Copy
. Then we could clone each value in the slice when we want the largest
function to have ownership. Using the clone
function means we’re potentially making more heap allocations in the case of types that own heap data like String
, and heap allocations can be slow if we’re working with large amounts of data.
Validating References with Lifetimes
说生命周期就绕不开rust的引用与借用,可先阅读一遍下面的文章,回顾一下引用与借用的概念,再继续阅读本文章
2.5 References & Borrowing
One detail we didn’t discuss in the “References and Borrowing” section in Chapter 4 is that every reference in Rust has a lifetime, which is the scope for which that reference is valid. Most of the time, lifetimes are implicit and inferred, just like most of the time, types are inferred. We must annotate types when multiple types are possible. In a similar way, we must annotate lifetimes when the lifetimes of references could be related in a few different ways. Rust requires us to annotate the relationships using generic lifetime parameters to ensure the actual references used at runtime will definitely be valid.
Preventing Dangling References with Lifetimes
fn main() {
{
let r; {
let x = 5;
r = &x;
} println!("r: {}", r);
}
}
Note: The examples declare variables without giving them an initial value, so the variable name exists in the outer scope. At first glance, this might appear to be in conflict with Rust’s having no null values. However, if we try to use a variable before giving it a value, we’ll get a compile-time error, which shows that Rust indeed does not allow null values.
The outer scope declares a variable named r
with no initial value, and the inner scope declares a variable named x
with the initial value of 5. Inside the inner scope, we attempt to set the value of r
as a reference to x
. Then the inner scope ends, and we attempt to print the value in r
. This code won’t compile because the value r
is referring to has gone out of scope before we try to use it. Here is the error message:
$ 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 error: aborting due to previous error For more information about this error, try `rustc --explain E0597`.
error: could not compile `chapter10`. To learn more, run the command again with --verbose.
The variable x
doesn’t “live long enough.” The reason is that x
will be out of scope when the inner scope ends on line 7. But r
is still valid for the outer scope; because its scope is larger, we say that it “lives longer.” If Rust allowed this code to work, r
would be referencing memory that was deallocated when x
went out of scope, and anything we tried to do with r
wouldn’t work correctly. So how does Rust determine that this code is invalid? It uses a borrow checker.
3.5 Rust Generic Types, Traits, and Lifetimes的更多相关文章
- Effective Java 26 Favor generic types
Use generic types to replace the object declaration Add one or more type parameters to its declarati ...
- Rust 总章
1.1 Rust安装 3.5 Rust Generic Types, Traits, and Lifetimes 3.6 String 与 切片&str的区别 https://openslr. ...
- Effective Java 23 Don't use raw types in new code
Generic types advantage Parameterized type can provide erroneous check in compile time. // Parameter ...
- Effective Java 27 Favor generic methods
Static utility methods are particularly good candidates for generification. The type parameter list, ...
- C# Generic(转载)
型(generic)是C#语言2.0和通用语言运行时(CLR)的一个新特性.泛型为.NET框架引入了类型参数(type parameters)的概念.类型参数使得设计类和方法时,不必确定一个或多个具体 ...
- castle windsor学习----- Referencing types in XML 在xm文件中引用类型
当从xml引用installer的语法如下 <install type="Acme.Crm.Infrastructure.ServicesInstaller, Acme.Crm.Inf ...
- 06 Frequently Asked Questions (FAQ) 常见问题解答 (常见问题)
Frequently Asked Questions (FAQ) Origins 起源 What is the purpose of the project? What is the history ...
- CGAL Manual/tutorial_hello_world.html
Hello World Author CGAL Editorial Board 本教程是为知道C++和几何算法的基本知识的CGAL新手准备的.第一节展示了如何特化点和段CGAL类,以及如何应用几何谓词 ...
- Java 8 Features – The ULTIMATE Guide--reference
Now, it is time to gather all the major Java 8 features under one reference post for your reading pl ...
随机推荐
- HydroD:辅助脚本函数
HydroD:辅助函数 在HydroD中,使用JS脚本可以快速进行模拟参数设置,但是经过尝试,HydroD中的JS脚本语言并不支持现在JavaScript中的一些语法.所以考虑采用Matlab字符串拼 ...
- C++ 重载、重写、重定义的区别
C++ 中 重载.重写.重定义的区别 重载(overload) 定义: 在同一个作用域内,两函数的函数名相同, 参数不相同(可以是参数类型不同或者是参数个数不同), 那么就说这两个 函数重载. 分类: ...
- 【python+postman接口自动化测试】(1)网络基础知识
一.IP地址 就像每个人都有一个身份证号码 IP地址是IP协议提供的一种统一的地址格式,它为互联网上的每一个网络和每一台主机分配一个逻辑地址. 查看IP命令: Windows: ipconfig Li ...
- java meil
import java.util.Date; import java.util.List; import java.util.Properties; import javax.activation.D ...
- 菜鸡的Java笔记 - java 线程常用操作方法
线程常用操作方法 线程的命名操作,线程的休眠,线程的优先级 线程的所有操作方法几乎都在 Thread 类中定义好了 线程的命名和取得 ...
- 菜鸡的Java笔记 第九 - java 接收键盘输入
package mysterious; import java.util.Scanner; public class lianxi { public static void hhh (){ Scann ...
- python实现高斯滤波
一,定义 核是:3 *3 均值滤波 二,高斯函数 Y方向的方差与X方向的一致.处理后图像看起来更模糊(滤波明显)的话,核要更大. (三)代码实现 (四)核计算 (五)图像产生高斯噪声循环代码实 ...
- [luogu3334]抛硬币
(数据范围的公式渲染有一些问题,大概是$a\le b\le 100$) 同洛谷4548,推导过程省略,直接给出答案-- 令$p_{H}=\frac{b}{a}$,$p_{T}=\frac{b}{b-a ...
- [atARC098F]Donation
贪心,一定在最后一次经过某节点时付出$b_{u}$,条件是付出后$W\ge \max(a_{i}-b_{i},0)$(同时也可以仅考虑这个限制,因为$W$在过程中不会增大) 假设"最后一次经 ...
- JAVA基础----面向对象复习和IDEA的安装和使用
1.使用集成开发工具eclipse 1.1.java的集成开发工具很多,包括:eclipse.Intellij IDEA.netbeans..... eclipse: IBM开发的.eclipse翻译 ...