1、提取函数消除重复
重复代码
fn main() {
let number_list = vec![34,50,25,100,65];
let mut largest = number_list[0];
for number in number_list {
if number > largest {
largest = number
}
}
println!("The largest number is {}",largest);
let number_list = vec![34,50,25,100,65,646,871,69];
let mut largest = number_list[0];
for number in number_list {
if number > largest {
largest = number
}
}
println!("The largest number is {}",largest);
}
这两部分的代码是一样的,那么我们如何进行优化来消除重复代码呢?
fn main() {
let number_list = vec![34,50,25,100,65];
let result = largest(&number_list);
println!("The largest number is {}",result);
let number_list = vec![34,50,25,100,65,646,871,69];
let result = largest(&number_list);
println!("The largest number is {}",result);
}
fn largest(list:&[i32]) ->i32{
let mut largest = list[0];
for &item in list {
if item > largest {
largest = item;
}
}
largest
}
消除重复的步骤
识别重复代码
提取重复代码到函数体中,并在函数签名中指定函数的输入和返回值
将重复的代码使用函数调用进行替代
2、泛型
泛型:提高代码复用能力
处理重复代码的问题
泛型是具体类型或其它属性的抽象代替:
你编写的代码不是最终的代码,而是一种模板,里面有一些“占位符”。
编译器在编译时将“占位符”替换为具体的类型。
例如: fn largest<T>(list: &[T]) ->T{ ...}类型参数:
很短,通常一个字母- CamelCase
T: type的缩写
函数定义中的泛型
泛型函数:
参数类型
返回类型
fn main() {
let number_list = vec![34,50,25,100,65];
let result = largest(&number_list);
println!("The largest number is {}",result);
let number_list = vec!['a','e','b','d','r','q'];
let result = largest(&number_list);
println!("The largest number is {}",result);
}
fn largest<T>(list:&[T]) ->T{
let mut largest = list[0];
for &item in list {
if item > largest {
largest = item;
}
}
largest
}
这里我们演示了泛型的使用,但是上面的代码会报错的,后面我们在说这些问题怎么解决
Struct定义中的泛型
struct Point<T> {
x:T,
y:T,
}
fn main() {
let integer = Point {x:5,y:10};
let float = Point {x:1.0,y:4.0};
}
可以使用多个泛型的类型参数
太多类型参数:你的代码需要重组为多个更小的单元
Enum定义中的泛型
可以让枚举的变体持有泛型数据类型
例如Option<T>,Result<T,E>
enum Option<T> {
Some<T>,
None,
}
enum Result<T,E> {
ok<T>,
Err<E>,
}
fn main() {
}
方法定义中的泛型
为struct或enum实现方法的时候,可在定义中使用泛型
struct Point<T> {
x:T,
y:T
}
impl<T> Point<T> {
fn x(&self)->&T {
&self.x
}
}
fn main() {
let p = Point{x:5,y:10};
println!("{}",p.x())
}
注意:
把T放在 impl关键字后,表示在类型T上实现方法
例如: impl<T> Point<T>
只针对具体类型实现方法(其余类型没实现方法):
例如: impl Point<f32>
struct里的泛型类型参数可以和方法的泛型类型参数不同
struct Point<T,U> {
x:T,
y:U
}
impl<T,U> Point<T,U> {
fn mixup<V,W>(self,other:Point<V,W>) -> Point<T,W> {
Point {
x:self.x,
y:other.y
}
}
}
fn main() {
let p1 = Point{x:5,y:4};
let p2 = Point{x:"Hello",y:'c'};
let p3 = p1.mixup(p2);
println!("p3.x={},p3.y={}",p3.x,p3.y);
}
泛型代码的性能
使用泛型的代码和使用具体类型的代码运行速度是一样的。
单态化(monomorphization)
在编译时将泛型替换为具体类型的过程
fn main() {
let integer = Some(5);
let float = Some(5.0);
}
enum Option_i32{
Some(i32),
None,
}
enum Option_f64{
Some(f64),
None,
}
fn main() {
let integer = Option_i32::Some(5);
let float = Option_f64::Some(5.0);
}
3、trait(特型)
Trait告诉Rust 编译器:
某种类型具有哪些并且可以与其它类型共享的功能
Trait:抽象的定义共享行为
Trait bounds(约束):泛型类型参数指定为实现了特定行为的类型
Trait与其它语言的接口(interface)类似,但有些区别。
定义一个Trait
Trait的定义:把方法签名放在一起,来定义实现某种目的所必需的一组行为。
关键字: trait
只有方法签名,没有具体实现
trait可以有多个方法:每个方法签名占一行,以;结尾
实现该trait的类型必须提供具体的方法实现
pub trait Summary {
fn summarize(&self) -> String;
fn summarize1(&self) -> String;
}
// NewsArticle
// Tweet
fn main() {
}
在类型上实现 trait
与为类型实现方法类似。不同之处:
- impl Xxxx for Tweet { ...}
在impl的块里,需要对Trait里的方法签名进行具体的实现
lib.rs:
pub trait Summary {
fn summarize(&self) -> String;
}
// NewsArticle
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)
}
}
// Tweet
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)
}
}
main.rs:
use guessing_game::{Summary, Tweet};
fn main() {
let tweet = Tweet {
username:String::from("horse_book"),
content:String::from("of course,as you probably already know,people"),
reply:false,
retweet:false,
};
println!("I new tewwt: {}",tweet.summarize())
}
实现 trait的约束
可以在某个类型上实现某个trait的前提条件是:
这个类型或这个trait是在本地crate里定义的
无法为外部类型来实现外部的trait:
这个限制是程序属性的一部分(也就是一致性)
更具体地说是孤儿规则:之所以这样命名是因为父类型不存在。
此规则确保其他人的代码不能破坏您的代码。反之亦然。
如果没有这个规则,两个crate可以为同一类型实现同一个trait,Rust就不知道应该使用哪个实现了。
默认实现
lib.rs:
pub trait Summary {
// fn summarize(&self) -> String;
fn summarize(&self) -> String {
String::from("(Read more ..)")
}
}
// NewsArticle
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)
// }
}
// Tweet
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)
}
}
main.rs:
use guessing_game::{NewsArticle, Summary};
fn main() {
let news_article:NewsArticle = NewsArticle {
headline:String::from("快过年了"),
content:String::from("快过年了"),
author:String::from("我爱海鲸"),
location:String::from("在广州"),
};
println!("I new tweet: {}",news_article.summarize())
}
默认实现的方法可以调用trait中其它的方法,即使这些方法没有默认实现。
lib.rs:
pub trait Summary {
fn summarize_author(&self) -> String;
fn summarize(&self) -> String {
format!("(Read more ..),{}",self.summarize_author())
}
}
// NewsArticle
pub struct NewsArticle {
pub headline:String,
pub location:String,
pub author:String,
pub content:String,
}
impl Summary for NewsArticle {
fn summarize_author(&self) -> String {
format!("@{}",self.author)
}
}
// Tweet
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 summarize_author(&self) -> String {
format!("@{}",self.username)
}
}
main.rs:
use guessing_game::{NewsArticle, Summary};
fn main() {
let news_article:NewsArticle = NewsArticle {
headline:String::from("快过年了"),
content:String::from("快过年了"),
author:String::from("我爱海鲸"),
location:String::from("在广州"),
};
println!("I new tweet: {}",news_article.summarize())
}
注意:无法从方法的重写实现里面调用默认的实现。
Trait作为参数
impl Trait 语法:适用于简单情况
pub fn notify(item: impl Summary) {
println!("Break news! {}",item.summarize())
}
Trait bound语法:可用于复杂情况
pub fn notify<T:Summary>(item:T) {
println!("Break news! {}",item.summarize())
}
impl Trait语法是Trait bound的语法糖
使用+指定多个Trait bound
pub fn notify1(item1: impl Summary + Display) {
println!("Break news! {}",item1.summarize())
}
pub fn notify<T:Summary + Display>(item1:T,item2:T) {
println!("Break news! {}",item1.summarize())
}
Trait bound使用where子句
pub fn notify<T:Summary + Display,U:Debug + Display>(a:T,b:U) ->String{
format!("Break news! {}",a.summarize())
}
pub fn notify2<T:Summary + Display,U:Debug + Display>(a:T,b:U) ->String
where T:Summary + Display,
U:Debug + Display
{
format!("Break news! {}",a.summarize())
}
在方法签名后指定where子句
实现Trait作为返回类型
impl Trait语法
pub fn notify1(s:&str) -> impl Summary{
NewsArticle {
headline:String::from("快过年了"),
content:String::from("快过年了"),
author:String::from("我爱海鲸"),
location:String::from("在广州"),
}
}
注意:impl Trait只能返回确定的同一种类型,返回可能不同类型的代码会报错
pub fn notify1(flag:bool) -> impl Summary{
if flag {
NewsArticle {
headline:String::from("快过年了"),
content:String::from("快过年了"),
author:String::from("我爱海鲸"),
location:String::from("在广州"),
}
} else {
Tweet {
username:String::from("我爱海鲸"),
content:String::from("快过年了"),
reply:false,
retweet:false,
}
}
}
在上面的代码中就会报错
使用Trait Bound 的例子:
在之前的largest的例子中我们的代码会报错,现在我们可以来进行修复了
fn main() {
let number_list = vec![34,50,25,100,65];
let result = largest(&number_list);
println!("The largest number is {}",result);
let number_list = vec!['a','e','b','d','r','q'];
let result = largest(&number_list);
println!("The largest number is {}",result);
let number_list = vec![String::from("frwqas"),String::from("gfdsfas"),String::from("ffdsas")];
let result = largest(&number_list);
println!("The largest number is {}",result);
}
fn largest<T:PartialOrd+Clone>(list:&[T]) ->&T{
let mut largest = &list[0];
for item in list {
if item > &largest {
largest = item;
}
}
largest
}
使用Trait Bound有条件的实现方法
在使用泛型类型参数的impl块上使用Trait bound,我们可以有条件的为实现了特定Trait的类型来实现方法
use std::fmt::Display;
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 cmd_display(&self) {
if self.x >= self.y {
println!("The largest member is x={}",self.x);
} else {
println!("The largest member is y={}",self.y);
}
}
}
fn main() {
}
也可以为实现了其它Trait的任意类型有条件的实现某个Trait
为满足Trait Bound的所有类型上实现Trait叫做覆盖实现(blanket implementations)
我们直接看标准库里的方法:
impl<T: fmt::Display + ?Sized> ToString for T {
// A common guideline is to not inline generic functions. However,
// removing `#[inline]` from this method causes non-negligible regressions.
// See <https://github.com/rust-lang/rust/pull/74852>, the last attempt
// to try to remove it.
#[inline]
default fn to_string(&self) -> String {
let mut buf = String::new();
let mut formatter = core::fmt::Formatter::new(&mut buf);
// Bypass format_args!() to avoid write_str with zero-length strs
fmt::Display::fmt(self, &mut formatter)
.expect("a Display implementation returned an error unexpectedly");
buf
}
}
这就是所谓的覆盖实现,就是我们可以为满足Display这个约束的ToString trait调用to_string这个方法