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

以Rust风格从函数返回传入或新建结构体的可变引用

问题分析与解决方案

你的核心问题是要编写一个函数,既可以接收外部的可变引用,也能在无输入时创建新实例,最终返回可修改的对象。原代码的错误在于:

  • 当complex_opt为None时,新建的Complex是函数局部变量,返回它的引用会导致悬垂引用(函数结束后局部变量被销毁);
  • 无法用单一生命周期标注统一两种情况:传入的引用生命周期由调用方控制,而新建实例的生命周期仅限于函数内部。

不需要用Arc(它适用于多线程共享所有权场景),最符合Rust idiom的方式是用自定义枚举封装两种所有权状态:要么持有外部可变引用,要么持有拥有所有权的实例。

实现代码

1. 定义枚举并实现解引用 trait

首先定义一个枚举来包装两种情况,同时实现Deref和DerefMut,让枚举可以像普通可变引用一样使用:

use std::ops::{Deref, DerefMut};

struct Complex {
    value: i32,
}

impl Complex {
    fn new() -> Complex {
        Complex { value: 3 }
    }

    fn setup(&mut self) {
        self.value = 2;
    }

    fn change(&mut self) {
        self.value += 5;
    }
}

// 封装可变引用或拥有所有权的实例
enum EitherMut<'a, T> {
    Borrowed(&'a mut T),
    Owned(T),
}

// 实现Deref,支持不可变访问
impl<'a, T> Deref for EitherMut<'a, T> {
    type Target = T;

    fn deref(&self) -> &Self::Target {
        match self {
            EitherMut::Borrowed(t) => t,
            EitherMut::Owned(t) => t,
        }
    }
}

// 实现DerefMut,支持可变访问
impl<'a, T> DerefMut for EitherMut<'a, T> {
    fn deref_mut(&mut self) -> &mut Self::Target {
        match self {
            EitherMut::Borrowed(t) => t,
            EitherMut::Owned(t) => t,
        }
    }
}

2. 修改目标函数

调整do_it函数,返回上述枚举类型,统一处理两种输入情况:

fn do_it(complex_opt: Option<&mut Complex>) -> EitherMut<'_, Complex> {
    let mut complex = match complex_opt {
        Some(c) => EitherMut::Borrowed(c),
        None => {
            let mut c = Complex::new();
            c.setup();
            EitherMut::Owned(c)
        }
    };

    // 直接调用可变方法,DerefMut自动解引用
    complex.change();

    complex
}

3. 使用示例

调用函数时,可以像操作普通Complex可变引用一样使用返回值:

fn main() {
    // 情况1:传入外部可变引用
    let mut existing = Complex { value: 10 };
    let mut result1 = do_it(Some(&mut existing));
    result1.change();
    println!("Existing value: {}", existing.value); // 输出15

    // 情况2:无输入,创建新实例
    let mut result2 = do_it(None);
    println!("New value: {}", result2.value); // 输出7
}

为什么这是正确的方式

  • 枚举明确区分了两种所有权状态,避免了悬垂引用问题;
  • 通过Deref和DerefMut trait,枚举的使用方式与普通可变引用完全一致,无需额外适配代码;
  • 没有运行时开销,比使用内部可变性(如RefCell)或引用计数(如Arc)更高效。

内容的提问来源于stack exchange,提问作者Jan Diederich

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.15 00:37:46