文盘Rust -- struct 中的生命周期

最近在用rust 写一个redis的数据校验工具。redis-rs中具备 redis::ConnectionLike trait,借助它可以较好的来抽象校验过程。在开发中,不免要定义struct 中的某些元素为 trait object,从而带来一些rust语言中的生命周期问题。 本文不具体讨论 redis的数据校验过程,通过一个简单的例子来聊聊 struct 中 trait object 元素的生命周期问题。

首先来定义一个 base trait,该 trait 中只包含一个函数,返回String类型。

1pub trait Base { 2 fn say(&self) -> String; 3}

接下来,定义两个实现了 Base trait 的 struct AFromBase 和 BFromBase

1pub struct AFromBase { 2 content: String, 3} 4 5impl Base for AFromBase { 6 fn say(&self) -> String { 7 self.content.clone() 8 } 9} 10 11pub struct BFromBase { 12 text: String, 13} 14 15impl Base for BFromBase { 16 fn say(&self) -> String { 17 self.text.clone() 18 } 19}

接下来,定义一个struct 包含两个 Base trait 的 trait object ,然后实现一个函数是 say 函数输出的字符串的拼接结果. 按照其他没有生命周期语言的编写习惯,直觉上这么写

1pub struct AddTowBase { 2 a: &mut dyn Base, 3 b: &mut dyn Base, 4} 5 6impl AddTowBase { 7 fn add(&self) -> String { 8 let result = self.a.say() + &self.b.say(); 9 result 10 } 11}

最后,搞个main函数验证一下。 完整代码如下

1pub trait Base { 2 fn say(&self) -> String; 3} 4 5pub struct AFromBase { 6 content: String, 7} 8 9impl Base for AFromBase { 10 fn say(&self) -> String { 11 self.content.clone() 12 } 13} 14 15pub struct BFromBase { 16 text: String, 17} 18 19impl Base for BFromBase { 20 fn say(&self) -> String { 21 self.text.clone() 22 } 23} 24 25pub struct AddTowBase { 26 a: &mut dyn Base, 27 b: &mut dyn Base, 28} 29 30impl<'a> AddTowBase<'a> { 31 fn add(&self) -> String { 32 let result = self.a.say() + &self.b.say(); 33 result 34 } 35} 36 37fn main() { 38 let mut a = AFromBase { 39 content: "baseA".to_string(), 40 }; 41 42 let mut b = BFromBase { 43 text: "baseB".to_string(), 44 }; 45 46 let addtow = AddTowBase { 47 a: &mut a, 48 b: &mut b, 49 }; 50 let r = addtow.add(); 51 println!("{}", r); 52}

很遗憾,以上代码是不能编译通过的,编译时报如下错误

1error[E0106]: missing lifetime specifier 2 --> examples/lifetimeinstruct.rs:26:8 3 | 426 | a: &mut dyn Base, 5 | ^ expected named lifetime parameter 6 | 7help: consider introducing a named lifetime parameter 8 | 925 ~ pub struct AddTowBase<'a> { 1026 ~ a: &'a mut dyn Base, 11 | 12 13error[E0106]: missing lifetime specifier 14 --> examples/lifetimeinstruct.rs:27:8 15 | 1627 | b: &mut dyn Base, 17 | ^ expected named lifetime parameter 18 | 19help: consider introducing a named lifetime parameter 20 | 2125 ~ pub struct AddTowBase<'a> { 2226 | a: &mut dyn Base, 2327 ~ b: &'a mut dyn Base, 24 | 25 26For more information about this error, try `rustc --explain E0106`. 27error: could not compile `wenpan-rust` due to 2 previous errors

编译器给出的提示很明确,要在 trait object 上添加生命周期参数,确保 struct 和他的 trait object 元素在同一生命周期,避免悬垂指针。 我们按照编译器的提示修改代码

1pub struct AddTowBase<'a> { 2 a: &'a mut dyn Base, 3 b: &'a mut dyn Base, 4} 5 6impl<'a> AddTowBase<'a> { 7 fn add(self) -> String { 8 let result = self.a.say() + &self.b.say(); 9 result 10 } 11}

代码顺利通过编译。 rust 的生命周期保证了内存的安全性,同时也增加了开发者的心智负担。是在上线之前多费心思写代码,还是在上线以后忙忙活活查问题,这是个 trade off 问题。俗话讲:"背着抱着,一样沉".我本人还是倾向于把问题控制在上线之前,少折腾用户。

本期咱们先聊到这儿,下期见

点赞
收藏

评论区

加载中...

相关推荐

Oracle 分组与拼接字符串同时使用

SELECTT.,ROWNUMIDFROM(SELECTT.EMPLID,T.NAME,T.BU,T.REALDEPART,T.FORMATDATE,SUM(T.S0)S0,MAX(UPDATETIME)CREATETIME,LISTAGG(TOCHAR(

皕杰报表之UUID

​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为

Rust学习笔记#1:一个猜谜游戏小项目

!(https://oscimg.oschina.net/oscnet/up51ea0c90f2725c56195c3eee5024b2402b2.JPEG)在深入探索Rust语法的细枝末节之前,先通过一个麻雀虽小但五脏俱全的小项目来整体把握Rust,这样可以避免迷失在细节的海洋中。我们可能会通过这个小项目一下子接触到很多新概念,但不必惊慌,我们只

Node.js 中使用 ECDSA 签名遇到的坑

文/Fenying最近有个朋友问我关于Node.js下使用ECDSA的问题,主要是使用Node.js的Crypto模块无法校验网络传输过来的签名结果。在踩坑无数后,终于搞清楚了原因。坑0x00:签名输出格式在排除了证书、消息不一致的可能之后,我开始对比使用Node.js签名的结果与网络传输过来的签

Linux应急响应(二):捕捉短连接

0x00前言​短连接(shortconnnection)是相对于长连接而言的概念,指的是在数据传送过程中,只在需要发送数据时,才去建立一个连接,数据发送完成后,则断开此连接,即每次连接只完成一项业务的发送。在系统维护中,一般很难去察觉,需要借助网络安全设备或者抓包分析,才能够去发现。0x01应急场景​

rust入坑指南之ownership

这篇文章我们介绍一下rust的一个核心概念ownership。Ownership是Rust语言的一个核心概念,它决定了一个值在程序中的生命周期以及对其访问权限的限制。