"首次提交"
This commit is contained in:
1
trait_learn/.gitignore
vendored
Executable file
1
trait_learn/.gitignore
vendored
Executable file
@ -0,0 +1 @@
|
||||
/target
|
8
trait_learn/Cargo.toml
Executable file
8
trait_learn/Cargo.toml
Executable file
@ -0,0 +1,8 @@
|
||||
[package]
|
||||
name = "trait_learn"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
116
trait_learn/src/lib.rs
Executable file
116
trait_learn/src/lib.rs
Executable file
@ -0,0 +1,116 @@
|
||||
/* 我们设计一个summary trait我们希望这个trait可以提供一个摘要的功能,
|
||||
比如我们的博客,微博,文章等都需要一个摘要,方便在信息流展示时进行展示。
|
||||
*/
|
||||
pub trait Summary {
|
||||
//定义一个summary trait下的方法 summerize,该方法返回一个摘要的字符串
|
||||
fn summarize(&self) -> String;
|
||||
/* trait 定义得到每个方法在使用了impl trait的数据结构中都需要完整实现所有的方法 */
|
||||
//fn reply(&self) -> bool; // 比如定义了这个方法后,下面引入了Summary的NewsArticle和Tweet都需要进行实现!
|
||||
|
||||
// 定义一个author方法,并将其实现,这就是trait的默认方法
|
||||
// 如果引入了summary的数据结构没有实现该方法,那么在调用时就会调用该默认方法。
|
||||
fn author(&self) -> String {
|
||||
String::from("unkown")
|
||||
}
|
||||
}
|
||||
|
||||
// 定义一个新闻文章的数据结构,
|
||||
pub struct NewsArticle {
|
||||
pub headline:String, // 标题
|
||||
pub location:String, // 地点
|
||||
pub author: String, // 作者
|
||||
pub content:String, // 内容
|
||||
}
|
||||
//定义一个推文的数据结构
|
||||
pub struct Tweet {
|
||||
pub username:String,//用户名
|
||||
pub content:String, // 内容
|
||||
pub reply: bool, //是否可回复
|
||||
pub retweet:bool, //是否可转发
|
||||
}
|
||||
|
||||
|
||||
//为 NewsArticle 实现 summary
|
||||
// 语法为 impl <trait name> for <data_type>
|
||||
impl Summary for NewsArticle {
|
||||
fn summarize(&self) -> String {
|
||||
//对于新闻来说我们使用标题,作者和地点。生成一个摘要信息
|
||||
format!("{}, by {} ({})",self.headline, self.author(), self.location)
|
||||
}
|
||||
|
||||
//覆盖掉Summary的默认author实现。在NewsArticle 实例调用该方法时只能调用到该函数,
|
||||
//并且该函数无法再调用到默认的author函数
|
||||
fn author(&self) -> String {
|
||||
let mut one = String::new();
|
||||
self.author.clone_into(&mut one);
|
||||
one
|
||||
}
|
||||
}
|
||||
|
||||
impl Summary for Tweet {
|
||||
fn summarize(&self) -> String {
|
||||
//对于推文来说我们使用作者和推文内容作为摘要
|
||||
format!("{}: {}", self.author(), self.content)
|
||||
}
|
||||
}
|
||||
|
||||
/* 经过上面针对两种不同的数据结构对于summery的实现,
|
||||
我们可以使得他们都有一种共同的功能-> 生成摘要
|
||||
*/
|
||||
|
||||
// 通过引入Dsiplay trait来使得本地的数据结构Tweet具备 Display的功能
|
||||
use std::{fmt, fmt::{Display, Formatter}};
|
||||
impl Display for Tweet {
|
||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||
//实现Display trait的功能
|
||||
write!(f, "{}:\n {} \n reply:{} retweet:{}", self.username, self.content, self.reply, self.retweet)
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
我们指定的函数参数时,通常需要指定参数的类型。只有对应类型的数据才可以传入,
|
||||
RUST提供了另外一种限制参数类型的方式:impl trait。
|
||||
通过这种定义形参的方法可以使得只有实现了指定trait的类型才可以使用该函数,但是又不限制具体的数据类型,
|
||||
这种使用方式适合针对某个trait所做的功能,使得所有实现该trait的类型都可以免于实现类似的函数,减少代码。
|
||||
var: impl xxx
|
||||
*/
|
||||
//定义一个通知函数。只有具备summary trait的类型才可以作为参数传递进来。
|
||||
//这样的一个通用的通知函数,使得不用为每个想要通知的数据类型都写一个类似的函数
|
||||
pub fn notify(not: impl Summary){
|
||||
println!("new notification: [{}]", not.summarize());
|
||||
}
|
||||
|
||||
// 传入两个参数都用impl Summary修饰,表示not1和not2都是实现了Summary的类型,但是他们不一定是同一个数据类型。
|
||||
pub fn notify1(not1: impl Summary, not2: impl Summary){
|
||||
println!("new notification1: [{}]", not1.summarize());
|
||||
println!("new notification1: [{}]", not2.summarize());
|
||||
}
|
||||
|
||||
// 传入两个参数都都是类型T,T类型要求是符合Summary trait的类型,
|
||||
// 这里要注意:not1和not2都必须是同一种类型才可以
|
||||
pub fn notify2<T: Summary>(not1: T, not2: T){
|
||||
println!("new notification2: [{}]", not1.summarize());
|
||||
println!("new notification2: [{}]", not2.summarize());
|
||||
}
|
||||
|
||||
//要求参数同时满足多个trait的类型限制
|
||||
pub fn some_func0<T: Summary+Copy>(not1: T){
|
||||
}
|
||||
|
||||
//多个参数,每个参数各自限制不用的trait限制
|
||||
pub fn some_func<T: Summary+Copy, U: Summary + Display>(not1: T, not2: U){
|
||||
}
|
||||
//简化的写法使用Where来将限制提在函数签名外,使得函数名和参数列表比较靠近,where可以换行,多行来写
|
||||
pub fn some_func1<T, U>(not1: T, not2: U) -> i32
|
||||
where T: Summary+Copy, U: Summary + Display
|
||||
{
|
||||
0
|
||||
}
|
||||
|
||||
// 返回值同样可以限制为实现了某个trait的数据类型, 也可以多种trait限制叠加。
|
||||
// 返回一个实现了Summary trait和Copy类型的变量
|
||||
pub fn some_func2<T, U>(not1: T, not2: U) -> impl Summary+Copy
|
||||
where T: Summary+Copy, U: Summary + Display
|
||||
{
|
||||
not1
|
||||
}
|
137
trait_learn/src/main.rs
Executable file
137
trait_learn/src/main.rs
Executable file
@ -0,0 +1,137 @@
|
||||
use core::num;
|
||||
|
||||
use trait_learn::{self, NewsArticle, Summary, Tweet};
|
||||
fn main() {
|
||||
println!("Hello, world!");
|
||||
trait_learn();
|
||||
largest_test();
|
||||
displaytest();
|
||||
}
|
||||
|
||||
|
||||
/* trait:RUST中实现一些接口或者特性或者共享功能的机制
|
||||
在C中我们一般对外提供功能都是使用的一个头文件去申明接口,然后在提供一个实现了这个接口的lib,
|
||||
这样外部的代码可根据接口进行调用实现功能,而内部的具体实现以及内部的数据结构就屏蔽在lib里,
|
||||
对于rust来说,对外提供的接口是通过pub关键字进行暴露的,也能够达到上述的目的,那么trait是做什么用呢?
|
||||
|
||||
在我的理解trait是在接口之上的一个东西,也可以反过来理解,他定义了一个接口应当具备的功能。
|
||||
当我们定义某个trait后,所有实现了这个trait的数据结构他们在某种程度上就是有一种共同的功能,
|
||||
那么在实际使用时就可以利用这种共性去编写函数,减少对于不同类型相同功能的使用代码。
|
||||
|
||||
比如我们都熟知的 print, 它就是可以将所有支持了Display这个trait的数据结构都进行打印到终端,
|
||||
我们只需在实现某个数据结构时将这个Display trait实现,其即可作为参数传递给print函数!
|
||||
就这个Display来说。其就类似于C++中某个类重载了<<操作符,或者python中重载了 _str_()一样。
|
||||
|
||||
那么为什么要单独把trait这个概念领出来呢,而不是说像其他语言那样使用重载的概念,这是因为
|
||||
这个trait还可以作为参数的限制的一部分,也就是说,我们通过泛型可以接受各种数据类型,但是可能有些
|
||||
数据类型并不符合我们的要求,因为我们这个函数的具体实现完成的功能,必须要求类型具备某些属性才行,
|
||||
这时候在比如C++中可能会用模板的type list来限制,只有在list里面的类型才可以传入这个函数,
|
||||
类似这样
|
||||
template<T>
|
||||
typename enable_if<xxx>
|
||||
RUST是倒过来进行筛选,直接在泛型的标注上,标注只有具备这个trait的类型才可以被调用!
|
||||
类似这样
|
||||
T: impl xxx
|
||||
这样有什么好处?,我们可以不必为新增加了某个实现xxx的数据类型进行修改该函数的代码,
|
||||
*/
|
||||
fn trait_learn() {
|
||||
// 定义一个 新闻实例
|
||||
let news = NewsArticle {
|
||||
author: String::from("ekko.bao"),
|
||||
content: String::from("Th ShangHai Peenguins once again are the best hockey team in the NHL."),
|
||||
headline: String::from("Penguins win the stanley Cup Championship"),
|
||||
location: String::from("ShangHai, China"),
|
||||
};
|
||||
|
||||
// 定义一个tweet实例
|
||||
let tweet = Tweet {
|
||||
username: String::from("ekko.bao"),
|
||||
content: String::from("of course, as you probably already know, people."),
|
||||
reply: false,
|
||||
retweet: false,
|
||||
};
|
||||
|
||||
println!("1 new tweet: {}", tweet.summarize());
|
||||
println!("New article: {}", news.summarize());
|
||||
|
||||
// 对于实现了 Display的类型可直接进行打印
|
||||
println!("tweet:{}", tweet);
|
||||
//NewsArticle未实现Diaply trait,无法进行打印。
|
||||
//println!("article:{}", news);
|
||||
|
||||
//可以使用 notify ,因为 Tweet 和 NewsArticle 都实现了 Summary
|
||||
//trait_learn::notify(tweet);
|
||||
//trait_learn::notify(news);
|
||||
//无法使用notify:the trait bound `&str: Summary` is not satisfied
|
||||
//trait_learn::notify("new notify");
|
||||
|
||||
// news和tweet 并不是同一种类型.是不可以调用的 notify2的
|
||||
//trait_learn::notify2(news, tweet);
|
||||
|
||||
// news和tweet 都实现了Summary,可以调用notify1
|
||||
trait_learn::notify1(news, tweet);
|
||||
}
|
||||
/*
|
||||
trait 只能在对应的crate 里面才能够进行实现,比如trait是在src/lib.rs里面定义的所以,
|
||||
对应的impl也只能在lib.rs里面进行实现,不能在main.rs里面实现,
|
||||
这样可以防止其他人使用lib的时候恶意破坏lib的代码。
|
||||
如下的代码将会报错:
|
||||
*/
|
||||
// impl Summary for NewsArticle {
|
||||
// }
|
||||
|
||||
|
||||
//使用trait限制,只有实现了比较和拷贝的类型才可以call largest函数
|
||||
fn largest<T: PartialOrd + Copy>(list: &[T]) -> T {
|
||||
let mut lag = list[0];
|
||||
for &item in list.iter() {
|
||||
if lag < item { // PartialOrd trait
|
||||
lag = item; // Copy trait
|
||||
}
|
||||
}
|
||||
lag
|
||||
}
|
||||
fn largest_test() {
|
||||
let number_list = vec![24, 50, 35, 100 ,65];
|
||||
let result = largest(&number_list);
|
||||
println!("largest is {}", result);
|
||||
}
|
||||
|
||||
|
||||
struct Point {
|
||||
x:i32,
|
||||
y:i32
|
||||
}
|
||||
|
||||
use std::fmt;
|
||||
impl fmt::Display for Point{
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "({}, {})", self.x, self.y)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
pub trait AddStr {
|
||||
fn add_str(&self) -> String;
|
||||
}
|
||||
|
||||
// 为所有支持dispaly trait类型添加一个对于AddStr的支持
|
||||
// AddStr 这个示例trait的目的是为了加一个前导的字符串
|
||||
impl <T> AddStr for T
|
||||
where T: fmt::Display
|
||||
{
|
||||
fn add_str(&self) -> String{
|
||||
String::from("head str") + &self.to_string()
|
||||
}
|
||||
}
|
||||
|
||||
fn displaytest(){
|
||||
let var = Point{
|
||||
x:12,
|
||||
y:12,
|
||||
};
|
||||
|
||||
let str = var.to_string();
|
||||
let str1 = var.add_str();
|
||||
println!("{} -> {}", str, str1);
|
||||
}
|
Reference in New Issue
Block a user