"首次提交"

This commit is contained in:
2024-06-08 15:01:12 +08:00
parent 6e0f708d0a
commit 84349a2469
112 changed files with 3272 additions and 0 deletions

1
trait_learn/.gitignore vendored Executable file
View File

@ -0,0 +1 @@
/target

8
trait_learn/Cargo.toml Executable file
View 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
View 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());
}
// 传入两个参数都都是类型TT类型要求是符合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
View 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();
}
/* traitRUST中实现一些接口或者特性或者共享功能的机制
在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);
//无法使用notifythe 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);
}