Skip to content

rust study - 05 #61

@zhuzhh

Description

@zhuzhh

泛型
泛型,为了消除重复

泛型并不会使程序比具体类型运行的慢

Rust 通过在编译时进行泛型代码的 单态化(monomorphization)来保证效率。
单态化是一个通过填充编译时使用的具体类型,将通用代码转换为特定代码的过程。

泛型没运行时开销

Trait:定义共同行为
类似于 Java 里的 接口 (interface)

pub trait Summary {
    fn summarize(&self) -> String; // 分号隔开
}

aggregator 库

// src/lib.rs
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)
    }
}

引用

use aggregator::{Summary, Tweet};

fn main() {
    let tweet = Tweet {
        username: String::from("horse_ebooks"),
        content: String::from(
            "of course, as you probably already know, people",
        ),
        reply: false,
        retweet: false,
    };

    println!("1 new tweet: {}", tweet.summarize());
}

默认行为

pub trait Summary {
    fn summarize(&self) -> String {
        String::from("(Read more...)")
    }
}
pub trait Summary {
    fn summarize_author(&self) -> String;

    fn summarize(&self) -> String {
        format!("(Read more from {}...)", self.summarize_author())
    }
}

impl Summary for Tweet {
    fn summarize_author(&self) -> String {
        format!("@{}", self.username)
    }
}

// 调用 summarize 方法时,也会调用summarize_author方法

trait 作为参数

生命周期

let r;
{
    let x = 5;
    r = &x;
}
println!("r: {}", r);

会报错,x 在离开作用域时就会销毁,外部不能在使用其引用了

函数中的泛型生命周期

fn main() {
    let string1 = String::from("abcd");
    let string2 = "xyz";

    let result = longest(string1.as_str(), string2);
    println!("The longest string is {}", result);
}

fn longest(x: &str, y: &str) -> &str {
    if x.len() > y.len() {
        x
    } else {
        y
    }
}

以上代码会报错,报错原因是 longest return 了一个借来的值,但是函数签名没有明确借的是 x 还是 y
确实,longest 函数 x 还是 y,只有在运行时才能确定

为解决以上问题,可使用泛型生命周期参数来定义引用间的关系,以便借用检查器可以进行分析

生命周期注解并不改变任何引用的生命周期的长短
相反它们描述了多个引用生命周期相互的关系,而不影响其生命周期

基本语法:以撇号(')开头

&i32        // 引用
&'a i32     // 带有显式生命周期的引用
&'a mut i32 // 带有显式生命周期的可变引用

单个的生命周期注解本身没有多少意义,因为生命周期注解告诉 Rust 多个引用的泛型生命周期参数如何相互联系的

例如如果函数有一个生命周期 'ai32 的引用的参数 first。还有另一个同样是生命周期 'ai32 的引用的参数 second。这两个生命周期注解意味着引用 firstsecond 必须与这泛型生命周期存在得一样久

eg:

fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {
    if x.len() > y.len() {
        x
    } else {
        y
    }
}

两个参数,他们都是与生命周期 'a 存在一样长的字符串 slice,返回一个同样与生命周期 'a 一样长的字符串 slice
实际含义是 longest 函数返回的引用的生命周期 与 参数引用的值的生命周期的较小者一致
参数 xy 哪个生命周期短,返回值的生命长度和其保持一致

注意 longest 函数并不需要知道 x 和 y 具体会存在多久,而只需要知道有某个可以被 'a 替代的作用域将会满足这个签名
(后半句不是很理解)

生命周期注解只会出现在函数签名中,而不存在于函数体中的任何代码中
让函数签名包含生命周期约定意味着 Rust 编译器的工作变的更简单了

eg:

fn main() {
    let string1 = String::from("long string is long");

    {
        let string2 = String::from("xyz");
        let result = longest(string1.as_str(), string2.as_str());
        println!("The longest string is {}", result);
    }
}

正确输出 "long string is long"

bad case

fn main() {
    let string1 = String::from("long string is long");
    let result;
    {
        let string2 = String::from("xyz");
        result = longest(string1.as_str(), string2.as_str());
    }
    println!("The longest string is {}", result);
}

result 的生命周期和 string2 一致(string2 的生命周期比 string1短),所以函数返回是寿命最短的那个

fn longest<'a>(x: &'a str, y: &str) -> &'a str {
    x
}

如果函数总是返回第一个参数,如上代码,则必须指定 参数 x 和返回值注解,否则会报错

如果返回一个新生成的变量,即便指定生命周期,也会编译失败

fn longest<'a>(x: &str, y: &str) -> &'a str {
    let aa = String::from("really long string");
    aa.as_str()
}

aa 在 函数执行完毕后就被清空了,而我们尝试从函数返回一个 aa 的引用
无法指定生命周期参数来改变悬垂引用

最好的解决方案是返回一个有所有权的数据类型而不是一个引用,这样函数调用者就需要负责清理这个值

fn longest() -> str {
    let aa = String::from("really long string");
    aa.as_str() // 返回非引用的值
}

结构体中使用生命周期注解

struct ImportantExcerpt<'a> {
    part: &'a str,
}

fn main() {
    let novel = String::from("Call me Ishmael. Some years ago...");
    let first_sentence = novel.split('.').next().expect("Could not find a '.'");
    let i = ImportantExcerpt {
        part: first_sentence,
    };
}

生命周期消除

RAII 的思想是:资源的有效期与持有资源的对象的生命期严格绑定,即由对象的构造函数完成资源的分配,同时由析构函数完成资源的释放。在这种要求下,只要对象能正确的析构,就不会出现资源泄露问题。

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions