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 ...
随机推荐
- mysql查看数据库大小
要想知道每个数据库的大小的话,步骤如下: 1.进入information_schema 数据库(存放了其他的数据库的信息) use information_schema; 2.查询所有数据的大小: s ...
- k8s入坑之路(11)kubernetes服务发现
kubernetes访问场景 1.集群内部访问 2.集群内部访问外部 3.集群外部访问内部 1.集群内部访问 1.pod之间直接ip通讯(利用calico通过路由表经过三层将ip流量转发)由于容器之间 ...
- Jmeter 运行结果的csv文件生成报告
把运行结果保存到本地,下次可以直接用结果生成测试报告. 一.首先保证脚本能正常运行 二.本地创建csv文件,用来保存运行结果 三.察看结果树,选择本地文件(上一步创建好的csv文件),保存运行结果,如 ...
- geoserver控制服务访问权限-类似百度地图的key
目录 缘起 可行性分析 如何实现key验证访问 如何控制key能访问哪些地图服务? 如何实现服务器ip白名单 流程梳理 申请key 访问地图 实施步骤 拦截器设置 配置key验证规则 配置服务拦截规则 ...
- mybatis插入数据时处理为null的属性
在做项目的时候,数据库中的所有字段被设置为全都不能为null,但是在我们开发过程中,插入一些记录的时候,实体类中的一些字段如果页面没有传入,则默认就会被设置为null,这样的话,在执行插入语句的时候, ...
- 性能优化 | Go Ballast 让内存控制更加丝滑
关于 Go GC 优化的手段你知道的有哪些?比较常见的是通过调整 GC 的步调,以调整 GC 的触发频率. 设置 GOGC 设置 debug.SetGCPercent() 这两种方式的原理和效果都是一 ...
- 【linux系统】命令学习(八)bash 编程实战学习
常见shell : bash sh zsh windows: git bash cygwin MAC : terminal iterm netstat 是linux下用于显示网络状态的命令.通 ...
- [atARC063F]Snuke's Coloring 2
首先,可以通过将所有$x_{i}=0$都选择第1类,其余选第2类,构造出一个以$(0,0)$和$(1,h)$为左下角和右上角的矩形,答案即为$2h+2$,类似地还可以构造出$2w+2$ 若最终的矩形不 ...
- [gym102798F]Skeleton Dynamization
考虑对于第$i$层$x$与第$i+1$层所对应的点$y$,点$p$在前$i$层中当且仅当$p$到$x$比$p$到$y$距离小 由此,考虑枚举第一层的一个点以及对应到第二层的边,通过bfs就可以确定第一 ...
- [loj539]旅游路线
考虑枚举加油的位置,当确定某次在第$i$个位置加油后,且下一次到$j$加油,那么$i$到$j$必然会选择不超过$c_{i}$条边且最长的路径,记作$d_{i,j}$ 如果能求出$d_{i,j}$,再设 ...