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 ...
随机推荐
- 为什么IDEA不推荐你使用@Autowired ?
@Autowired注解相信每个Spring开发者都不陌生了!在DD的Spring Boot基础教程和Spring Cloud基础教程中也都经常会出现. 但是当我们使用IDEA写代码的时候,经常会发现 ...
- 四种 AI 技术方案,教你拥有自己的 Avatar 形象
大火的 Avatar到底是什么 ? 随着元宇宙概念的大火,Avatar 这个词也开始越来越多出现在人们的视野.2009 年,一部由詹姆斯・卡梅隆执导 3D 科幻大片<阿凡达>让很多人认识了 ...
- 【高并发】深入解析Callable接口
大家好,我是冰河~~ 本文纯干货,从源码角度深入解析Callable接口,希望大家踏下心来,打开你的IDE,跟着文章看源码,相信你一定收获不小. 1.Callable接口介绍 Callable接口是J ...
- void * 是什么?
最近遇到void *的问题无法解决,发现再也无法逃避了(以前都是采取悄悄绕过原则),于是我决定直面它. 在哪遇到了? 线程创建函数pthread_create()的最后一个参数void *arg,嗯? ...
- css盒模型简介
如何了解盒模型 盒模型简介:盒模型是css布局的基石,它规定了网页元素如何显示以及元素间相互关系.css定义所有的元素都可以拥有像盒子一样的外形和平面空间. 盒模型的组成:内容区.补白/填充.边框.边 ...
- 使用Adobe Acrobat进行Word转PDF遇到的问题及解决方法
软件版本:Adobe Acrobat 9 Pro 使用场景:Word转PDF 问题1: 我以为先要在Adobe Acrobat 9 Pro中打开Word文件,然后在执行类似转换/导出操作.但是始终无法 ...
- [cf10E]Greedy Change
对于$w$的表示方案,可以用序列描述,即$x_{i}$表示第$i$种货币的数量 贪心策略得到的方案即是(对应序列)字典序最大的方案,并定义最优策略得到的方案为在最小化货币总数的基础上,(对应序列)字典 ...
- 史上最简单的手写Promise,仅17行代码即可实现Promise链式调用
Promise的使用相比大家已经孰能生巧了,我这里就不赘述了 先说说我写的Promise的问题吧,无法实现宏任务和微任务里的正确执行(也就是在Promise里面写setTimeout,setInter ...
- 回顾Servlet开发
1.建立的文件 2.servlet package com.shao.servlet; import javax.servlet.ServletException; import javax.serv ...
- HCNP Routing&Switching之组播技术-组播基础
组播技术背景 随着internet网络的不断发展,网络中交互的各种数据.语音.视频信息数量突增:新型的在线直播.网络电视.视频会议等应用也在逐渐兴起:这些业务大多符合点到多点的模式,对信息安全性.传播 ...