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.

泛型的作用:降低代码冗余

  1. Identify duplicate code.
  2. Extract the duplicate code into the body of the function and specify the inputs and return values of that code in the function signature.
  3. Update the two instances of duplicated code to call the function instead.

Traits: Defining Shared Behavior

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的更多相关文章

  1. Effective Java 26 Favor generic types

    Use generic types to replace the object declaration Add one or more type parameters to its declarati ...

  2. Rust 总章

    1.1 Rust安装 3.5 Rust Generic Types, Traits, and Lifetimes 3.6 String 与 切片&str的区别 https://openslr. ...

  3. Effective Java 23 Don't use raw types in new code

    Generic types advantage Parameterized type can provide erroneous check in compile time. // Parameter ...

  4. Effective Java 27 Favor generic methods

    Static utility methods are particularly good candidates for generification. The type parameter list, ...

  5. C# Generic(转载)

    型(generic)是C#语言2.0和通用语言运行时(CLR)的一个新特性.泛型为.NET框架引入了类型参数(type parameters)的概念.类型参数使得设计类和方法时,不必确定一个或多个具体 ...

  6. castle windsor学习----- Referencing types in XML 在xm文件中引用类型

    当从xml引用installer的语法如下 <install type="Acme.Crm.Infrastructure.ServicesInstaller, Acme.Crm.Inf ...

  7. 06 Frequently Asked Questions (FAQ) 常见问题解答 (常见问题)

    Frequently Asked Questions (FAQ) Origins 起源 What is the purpose of the project? What is the history ...

  8. CGAL Manual/tutorial_hello_world.html

    Hello World Author CGAL Editorial Board 本教程是为知道C++和几何算法的基本知识的CGAL新手准备的.第一节展示了如何特化点和段CGAL类,以及如何应用几何谓词 ...

  9. 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 ...

随机推荐

  1. namaspace之pid namespace

    认识Namespace namespace 是 Linux 内核用来隔离内核资源的方式.通过 namespace 可以让一些进程只能看到与自己相关的一部分资源,而另外一些进程也只能看到与它们自己相关的 ...

  2. Java测试开发--sts安装Lombok(七)

    1.sts安装Lombok的步骤: 下载最新的lombok.jar包,进入cmd窗口,切到Lombok下载的目录,运行命令: java -jar lombok.jar,会出现如下界面: 已经默认选好了 ...

  3. 问题 C: A+B Problem II

    题目描述 I have a very simple problem for you. Given two integers A and B, your job is to calculate the ...

  4. 解决一个无聊的问题,如何处理Java用户在dos被收集信息时拷贝带换行符的文本信息造成的while的多次循环(java解决Scanner.next在接收用户输入时出现多个换行的形况)[解决方案一]

    问题描述: 用户在dos窗口输入的时候(web项目不会出现这样的问题,所以这个问题日常碰不到),摁下回车时,Scanner对象的next()扫描用户输入的文本,后面就可以根据输入的字符串进行判断,并执 ...

  5. 使用json.net实现复杂对象转换为QueryString

    目标:生成复杂对象的QueryString,比如 new { Field1 = 1, Field2 = new { Field3 = "2", Field4 = new[] { n ...

  6. [bzoj3171]循环格

    如果把这个矩阵看成一张图,题目相当于要求每一个点的入度和出度都是1(也就是有很多环),否则指向环的点就无法走回自己了将所有点拆成两个,S向原来的点流(1,0)的边,拆出来的点向T连(1,0)的边,然后 ...

  7. 第02章_MySQL环境搭建

    第02章_MySQL环境搭建 1. MySQL的卸载 步骤1:停止MySQL服务 在卸载之前,先停止MySQL8.0的服务.按键盘上的"Ctrl + Alt + Delete"组合 ...

  8. IPv4 寻址方式简介

    IPv4 支持三种不同类型的寻址模式.单播寻址方式.广播寻址方式和组播寻址方式.本章节我们来介绍这些寻址方式. 单播寻址方式 在这种模式下,数据只发送到一个目标主机.Destination Addre ...

  9. 职场工作方法论:目标管理SMART原则

    目标管理由管理学大师彼得·德鲁克在他的著作<管理实践>(The Practice of Management)一书中提出.SMART原则(Specific具体的, Measurable可衡 ...

  10. 论文翻译:2020_A Recursive Network with Dynamic Attention for Monaural Speech Enhancement

    论文地址:基于动态注意的递归网络单耳语音增强 论文代码:https://github.com/Andong-Li-speech/DARCN 引用格式:Li, A., Zheng, C., Fan, C ...