当前位置: 首页 > news >正文

Trait与生命周期

原文链接:(*´∇`*) 咦,又好了~ Rust – xiaocr_blogicon-default.png?t=N7T8http://www.xiaocr.fun/index.php/2024/03/18/trait%E4%B8%8E%E7%94%9F%E5%91%BD%E5%91%A8%E6%9C%9F/

目录

Trait

定义trait

默认实现

trait作为参数

Trait Bound语法

通过+指定多个 trait bound

通过where简化 trait bound

返回实现了 trait 的类型

生命周期

生命周期避免了悬垂引用

函数中泛型生命周期

生命周期标记语法

函数签名中的生命周期标注

结构体定义中生命周期标注

生命周期省略

静态生命周期

结合泛型类型参数、trait bounds 和生命周期


😃Trait

trait 告诉 Rust 编译器某个特定类型拥有可能与其他类型共享的功能。可以通过 trait 以一种抽象的方式定义共享的行为


注意:trait 类似于其他语言中常被称为 接口interfaces)的功能,虽然有一些不同。

❗️定义trait

一个类型的行为由其可供调用的方法构成。如果可以对不同类型调用相同的方法的话,这些类型就可以共享相同的行为了。trait 定义是一种将方法签名组合起来的方法,目的是定义一个实现某些目的所必需的行为的集合。

pub trait Summary {fn summarize(&self) -> String;
}

这里使用 trait 关键字来声明一个 trait,后面是 trait 的名字,在这个例子中是 Summary。在大括号中声明描述实现这个 trait 的类型所需要的行为的方法签名,在这个例子中是 fn summarize(&self) -> String。

默认实现


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

trait作为参数

使用 trait 来接受多种不同类型的参数

pub fn notify(item: impl Summary) {println!("Breaking news! {}", item.summarize());
}

对于 item 参数,我们指定了 impl 关键字和 trait 名称,而不是具体的类型。该参数支持任何实现了指定 trait 的类型。在 notify 函数体中,可以调用任何来自 Summary trait 的方法,比如 summarize。我们可以传递任何 NewsArticle 或 Tweet 的实例来调用 notify。任何用其它如 String 或 i32 的类型调用该函数的代码都不能编译,因为它们没有实现 Summary。

Trait Bound语法

impl Trait 语法适用于直观的例子,它实际上是一种较长形式语法的语法糖。我们称为 trait bound,它看起来像:

pub fn notify<T: Summary>(item: T) {println!("Breaking news! {}", item.summarize());
}

通过+指定多个 trait bound

如果 notify 需要显示 item 的格式化形式,同时也要使用 summarize 方法,那么 item 就需要同时实现两个不同的 trait:Display 和 Summary。这可以通过 + 语法实现:

pub fn notify(item: impl Summary + Display) {

+ 语法也适用于泛型的 trait bound:

pub fn notify<T: Summary + Display>(item: T) {

通过指定这两个 trait bound,notify 的函数体可以调用 summarize 并使用 {} 来格式化 item。

通过where简化 trait bound

每个泛型有其自己的 trait bound,所以有多个泛型参数的函数在名称和参数列表之间会有很长的 trait bound 信息,这使得函数签名难以阅读。为此,Rust 有另一个在函数签名之后的 where 从句中指定 trait bound 的语法

fn some_function<T, U>(t: T, u: U) -> i32where T: Display + Clone,U: Clone + Debug
{

返回实现了 trait 的类型

也可以在返回值中使用 impl Trait 语法,来返回实现了某个 trait 的类型:

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,}
}

生命周期

生命周期的概念从某种程度上说不同于其他语言中类似的工具,毫无疑问这是 Rust 最与众不同的功能。

生命周期避免了悬垂引用

生命周期的主要目标是避免悬垂引用,它会导致程序引用了非预期引用的数据

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

会出现错误,:“x dose not live long enough

函数中泛型生命周期

下面这个代码是会报错的

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}
}

因为 Rust 并不知道将要返回的引用是指向 x 或 y。事实上我们也不知道,因为函数体中 if 块返回一个 x 的引用而 else 块返回一个 y 的引用!

当我们定义这个函数的时候,并不知道传递给函数的具体值,所以也不知道到底是 if 还是 else 会被执行。我们也不知道传入的引用的具体生命周期

77c47f15aef222b134cb6484a0a7a6b9.png

生命周期标记语法

生命周期标注并不改变任何引用的生命周期的长短。与当函数签名中指定了泛型类型参数后就可以接受任何类型一样,当指定了泛型生命周期后函数也能接受任何生命周期的引用。生命周期标注描述了多个引用生命周期相互的关系,而不影响其生命周期。

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

函数签名中的生命周期标注

就像泛型类型参数,泛型生命周期参数需要声明在函数名和参数列表间的尖括号中

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

结构体定义中生命周期标注

需要为结构体定义中的每一个引用添加生命周期标注

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 };
}

生命周期省略

在编写了很多 Rust 代码后,Rust 团队发现在特定情况下 Rust 开发者们总是重复地编写一模一样的生命周期标注。这些场景是可预测的并且遵循几个明确的模式。生命周期省略规则lifetime elision rules)

  • 第一条规则是每一个是引用的参数都有它自己的生命周期参数。换句话说就是,有一个引用参数的函数有一个生命周期参数:fn foo<'a>(x: &'a i32),有两个引用参数的函数有两个不同的生命周期参数,fn foo<'a, 'b>(x: &'a i32, y: &'b i32),依此类推。
  • 第二条规则是如果只有一个输入生命周期参数,那么它被赋予所有输出生命周期参数:fn foo<'a>(x: &'a i32) -> &'a i32。
  • 第三条规则是如果方法有多个输入生命周期参数并且其中一个参数是 &self 或 &mut self,说明是个对象的方法(method)

静态生命周期

'static,其生命周期能够存活于整个程序期间。所有的字符串字面量都拥有 'static 生命周期

fn main() {
let s: &'static str = "I have a static lifetime.";
}

结合泛型类型参数、trait bounds 和生命周期

fn main() {
use std::fmt::Display;fn longest_with_an_announcement<'a, T>(x: &'a str, y: &'a str, ann: T) -> &'a strwhere T: Display
{println!("Announcement! {}", ann);if x.len() > y.len() {x} else {y}
}
}

相关文章:

  • 学习vue3 第四章(reactive全家桶)
  • playwright自动化项目搭建
  • laravel(源码笔记)服务绑定和解析(依赖注入-反射,控制反转)
  • 【DFS+贪心】第十四届蓝桥杯省赛C++ B组《飞机降落》(C++)
  • wordpress给指定ID分类添加特定的字段
  • 【skimage包如何安装】
  • CentOS7使用Docker部署.net Webapi
  • python云上水果超市的设计与实现flask-django-php-nodejs
  • C/C++代码性能优化——数据结构和算法
  • 云手机为电商提供五大出海优势
  • 企业数字化转型:是竞争力的关键,还是行业炒作?
  • web自动化测试框架都是有哪些?
  • vim | 介绍vim以及配置vimrc文件
  • 【C语言】C语言运算符优先级详解
  • 汽车制造产生的污废水如何处理排放
  • [nginx文档翻译系列] 控制nginx
  • 【Leetcode】104. 二叉树的最大深度
  • Java反射-动态类加载和重新加载
  • Laravel深入学习6 - 应用体系结构:解耦事件处理器
  • php面试题 汇集2
  • RedisSerializer之JdkSerializationRedisSerializer分析
  • Wamp集成环境 添加PHP的新版本
  • webpack+react项目初体验——记录我的webpack环境配置
  • 关于字符编码你应该知道的事情
  • 官方新出的 Kotlin 扩展库 KTX,到底帮你干了什么?
  • 简单基于spring的redis配置(单机和集群模式)
  • 开源地图数据可视化库——mapnik
  • 理清楚Vue的结构
  • 罗辑思维在全链路压测方面的实践和工作笔记
  • 前端面试题总结
  • 使用Envoy 作Sidecar Proxy的微服务模式-4.Prometheus的指标收集
  • 智能网联汽车信息安全
  • 转载:[译] 内容加速黑科技趣谈
  • [Shell 脚本] 备份网站文件至OSS服务(纯shell脚本无sdk) ...
  • const的用法,特别是用在函数前面与后面的区别
  • 宾利慕尚创始人典藏版国内首秀,2025年前实现全系车型电动化 | 2019上海车展 ...
  • 如何通过报表单元格右键控制报表跳转到不同链接地址 ...
  • $$$$GB2312-80区位编码表$$$$
  • (+3)1.3敏捷宣言与敏捷过程的特点
  • (3)llvm ir转换过程
  • (51单片机)第五章-A/D和D/A工作原理-A/D
  • (Ruby)Ubuntu12.04安装Rails环境
  • (二)springcloud实战之config配置中心
  • (附源码)ssm高校志愿者服务系统 毕业设计 011648
  • (附源码)ssm考试题库管理系统 毕业设计 069043
  • (汇总)os模块以及shutil模块对文件的操作
  • (机器学习-深度学习快速入门)第一章第一节:Python环境和数据分析
  • (免费领源码)python+django+mysql线上兼职平台系统83320-计算机毕业设计项目选题推荐
  • (转)Android中使用ormlite实现持久化(一)--HelloOrmLite
  • (转)es进行聚合操作时提示Fielddata is disabled on text fields by default
  • .bat批处理(四):路径相关%cd%和%~dp0的区别
  • .NET Core跨平台微服务学习资源
  • .net MVC中使用angularJs刷新页面数据列表
  • .NET 事件模型教程(二)
  • .NET 中 GetProcess 相关方法的性能