• 设为首页
  • 点击收藏
  • 手机版
    手机扫一扫访问
    迪恩网络手机版
  • 关注官方公众号
    微信扫一扫关注
    公众号

3.5 Rust Generic Types, Traits, and Lifetimes

原作者: [db:作者] 来自: [db:来源] 收藏 邀请

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.

 


鲜花

握手

雷人

路过

鸡蛋
该文章已有0人参与评论

请发表评论

全部评论

专题导读
上一篇:
Rust生命周期bound用于泛型的引用发布时间:2022-07-18
下一篇:
Matlab如何创建带标记的线图发布时间:2022-07-18
热门推荐
阅读排行榜

扫描微信二维码

查看手机版网站

随时了解更新最新资讯

139-2527-9053

在线客服(服务时间 9:00~18:00)

在线QQ客服
地址:深圳市南山区西丽大学城创智工业园
电邮:jeky_zhao#qq.com
移动电话:139-2527-9053

Powered by 互联科技 X3.4© 2001-2213 极客世界.|Sitemap