文盘Rust — struct 中的生命周期

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

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

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

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

pub struct AFromBase {     content: String, }  impl Base for AFromBase {     fn say(&self) -> String {         self.content.clone()     } }  pub struct BFromBase {     text: String, }  impl Base for BFromBase {     fn say(&self) -> String {         self.text.clone()     } } 

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

pub struct AddTowBase {     a: &mut dyn Base,     b: &mut dyn Base, }  impl AddTowBase {     fn add(&self) -> String {         let result = self.a.say() + &self.b.say();         result     } } 

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

pub trait Base {     fn say(&self) -> String; }  pub struct AFromBase {     content: String, }  impl Base for AFromBase {     fn say(&self) -> String {         self.content.clone()     } }  pub struct BFromBase {     text: String, }  impl Base for BFromBase {     fn say(&self) -> String {         self.text.clone()     } }  pub struct AddTowBase {     a: &mut dyn Base,     b: &mut dyn Base, }  impl<'a> AddTowBase<'a> {     fn add(&self) -> String {         let result = self.a.say() + &self.b.say();         result     } }  fn main() {     let mut a = AFromBase {         content: "baseA".to_string(),     };      let mut b = BFromBase {         text: "baseB".to_string(),     };      let addtow = AddTowBase {         a: &mut a,         b: &mut b,     };     let r = addtow.add();     println!("{}", r); } 

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

error[E0106]: missing lifetime specifier   --> examples/lifetimeinstruct.rs:26:8    | 26 |     a: &mut dyn Base,    |        ^ expected named lifetime parameter    | help: consider introducing a named lifetime parameter    | 25 ~ pub struct AddTowBase<'a> { 26 ~     a: &'a mut dyn Base,    |  error[E0106]: missing lifetime specifier   --> examples/lifetimeinstruct.rs:27:8    | 27 |     b: &mut dyn Base,    |        ^ expected named lifetime parameter    | help: consider introducing a named lifetime parameter    | 25 ~ pub struct AddTowBase<'a> { 26 |     a: &mut dyn Base, 27 ~     b: &'a mut dyn Base,    |  For more information about this error, try `rustc --explain E0106`. error: could not compile `wenpan-rust` due to 2 previous errors 

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

pub struct AddTowBase<'a> {     a: &'a mut dyn Base,     b: &'a mut dyn Base, }  impl<'a> AddTowBase<'a> {     fn add(self) -> String {         let result = self.a.say() + &self.b.say();         result     } } 

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

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

发表评论

相关文章