You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Rust函数模拟工具生命周期配置问题求助

构建Rust Mock工具时的生命周期错误解决

问题

构建Rust模拟工具时,为KitchenSink trait编写Mock实现,通过MockHelper处理参数匹配与调用追踪,运行时触发「lifetime may not live long enough」错误。

错误信息

error: lifetime may not live long enough
  --> src/lib.rs:73:9
   |
67 | impl<'a> KitchenSink for MockKitchenSink<'a> {
   |      -- lifetime `'a` defined here
...
72 |     fn convert_custom_ref_to_u32(&self, arg: &CustomStruct) -> u32 {
   |                                              - let's call the lifetime of this reference `'1`
73 |         self.convert_custom_ref_to_u32_calls.handle_call(arg)
   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ argument requires that `'1` must outlive `'a`

完整示例代码

/// Represents the expected function calls the mock will recieve,
/// for a function A -> B.
struct MockHelper<A, B> {
    fns: Vec<Box<dyn MockFunction<A, B>>>,
}

impl<A, B> Default for MockHelper<A, B> {
    fn default() -> Self {
        Self { fns: vec![] }
    }
}

impl<A, B> MockHelper<A, B> {
    /// Try calling each of the listed functions until
    /// one succeeds.
    fn handle_call(&self, arg: A) -> B
    where
        A: Copy,
    {
        for f in &self.fns {
            match f.try_call(arg) {
                Some(v) => return v,
                None => continue,
            }
        }
        panic!("No matching call")
    }

    fn add_call<T: MockFunction<A, B>>(&mut self, call: T)
    where
        T: 'static,
    {
        self.fns.push(Box::new(call))
    }
}

/// Trait to represent an expected function call
/// of the form A -> B
pub trait MockFunction<A, B> {
    fn try_call(&self, arg: A) -> Option<B>;
}

/// Simple argument/response pair
impl MockFunction<u32, u32> for (u32, u32) {
    fn try_call(&self, arg: u32) -> Option<u32> {
        if arg == self.0 {
            Some(self.1)
        } else {
            None
        }
    }
}

struct CustomStruct {}

pub trait KitchenSink {
    fn convert_u32_to_u32(&self, arg: u32) -> u32;
    fn convert_custom_ref_to_u32(&self, arg: &CustomStruct) -> u32;
}

#[derive(Default)]
pub struct MockKitchenSink<'a> {
    convert_u32_to_u32_calls: MockHelper<u32, u32>,
    convert_custom_ref_to_u32_calls: MockHelper<&'a CustomStruct, u32>,
}

impl<'a> KitchenSink for MockKitchenSink<'a> {
    fn convert_u32_to_u32(&self, arg: u32) -> u32 {
        self.convert_u32_to_u32_calls.handle_call(arg)
    }

    fn convert_custom_ref_to_u32(&self, arg: &CustomStruct) -> u32 {
        self.convert_custom_ref_to_u32_calls.handle_call(arg)
    }
}

pub fn main() {}

#[cfg(test)]
pub mod tests {
    use crate::{CustomStruct, KitchenSink as _, MockKitchenSink};

    #[test]
    fn kitchen_sink_generic() {
        let mut mock: MockKitchenSink = MockKitchenSink::default();
        mock.convert_u32_to_u32_calls.add_call((1, 4));

        let arg = CustomStruct {};
        assert_eq!(mock.convert_u32_to_u32(1), 4);
        assert_eq!(mock.convert_custom_ref_to_u32(&arg), 5);
    }
}

问题分析

错误核心在于:MockKitchenSink<'a>的convert_custom_ref_to_u32_calls字段被定义为MockHelper<&'a CustomStruct, u32>,它要求传入的引用生命周期必须长于或等于'a。但convert_custom_ref_to_u32方法接收的引用&CustomStruct是调用时产生的临时生命周期'1,Rust无法保证'1能覆盖'a的整个生命周期,因此触发错误。

解决方案

关键修改思路

  1. 移除MockKitchenSink的生命周期参数,避免将结构体生命周期与引用绑定
  2. 调整MockHelper和MockFunction的定义,支持处理任意生命周期的引用参数
  3. 适配MockFunction的实现,让其能正确匹配引用类型的参数

修改后的完整代码

/// Represents the expected function calls the mock will recieve,
/// for a function A -> B.
struct MockHelper<A, B> {
    fns: Vec<Box<dyn for<'r> MockFunction<&'r A, B>>>,
}

impl<A, B> Default for MockHelper<A, B> {
    fn default() -> Self {
        Self { fns: vec![] }
    }
}

impl<A, B> MockHelper<A, B> {
    /// Try calling each of the listed functions until
    /// one succeeds.
    fn handle_call(&self, arg: &A) -> B
    where
        A: PartialEq,
    {
        for f in &self.fns {
            match f.try_call(arg) {
                Some(v) => return v,
                None => continue,
            }
        }
        panic!("No matching call")
    }

    fn add_call<T>(&mut self, call: T)
    where
        T: for<'r> MockFunction<&'r A, B> + 'static,
    {
        self.fns.push(Box::new(call))
    }
}

/// Trait to represent an expected function call
/// of the form A -> B
pub trait MockFunction<A, B> {
    fn try_call(&self, arg: A) -> Option<B>;
}

/// Simple argument/response pair for u32
impl MockFunction<u32, u32> for (u32, u32) {
    fn try_call(&self, arg: u32) -> Option<u32> {
        if arg == self.0 {
            Some(self.1)
        } else {
            None
        }
    }
}

/// Simple argument/response pair for &CustomStruct
impl<'r> MockFunction<&'r CustomStruct, u32> for (CustomStruct, u32) {
    fn try_call(&self, arg: &'r CustomStruct) -> Option<u32> {
        // 可根据需求实现具体匹配逻辑,示例直接返回预设值
        Some(self.1)
    }
}

struct CustomStruct {}

pub trait KitchenSink {
    fn convert_u32_to_u32(&self, arg: u32) -> u32;
    fn convert_custom_ref_to_u32(&self, arg: &CustomStruct) -> u32;
}

#[derive(Default)]
pub struct MockKitchenSink {
    convert_u32_to_u32_calls: MockHelper<u32, u32>,
    convert_custom_ref_to_u32_calls: MockHelper<CustomStruct, u32>,
}

impl KitchenSink for MockKitchenSink {
    fn convert_u32_to_u32(&self, arg: u32) -> u32 {
        self.convert_u32_to_u32_calls.handle_call(&arg)
    }

    fn convert_custom_ref_to_u32(&self, arg: &CustomStruct) -> u32 {
        self.convert_custom_ref_to_u32_calls.handle_call(arg)
    }
}

pub fn main() {}

#[cfg(test)]
pub mod tests {
    use crate::{CustomStruct, KitchenSink as _, MockKitchenSink};

    #[test]
    fn kitchen_sink_generic() {
        let mut mock = MockKitchenSink::default();
        mock.convert_u32_to_u32_calls.add_call((1, 4));
        mock.convert_custom_ref_to_u32_calls.add_call((CustomStruct {}, 5));

        let arg = CustomStruct {};
        assert_eq!(mock.convert_u32_to_u32(1), 4);
        assert_eq!(mock.convert_custom_ref_to_u32(&arg), 5);
    }
}

修改说明

  • MockHelper使用for<'r> MockFunction<&'r A, B>语法,确保能适配任意生命周期的引用参数
  • 移除MockKitchenSink的生命周期参数,消除结构体与引用的生命周期绑定限制
  • 为&CustomStruct类型新增MockFunction实现,支持引用参数的匹配逻辑

内容的提问来源于stack exchange,提问作者Michael Anderson

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.17 06:45:56