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

能否通过生命周期在编译期追踪值归属者并限制跨归属传递?

如何追踪值的「归属者」,跨归属者传递时触发编译错误?

我想实现一种机制,让某个值(比如示例中的Foo)只能被创建它的「归属者」(比如Owner)接收,当把它传递给其他归属者时触发编译错误。我研究了不变生命周期和generativity crate,但能力还不足以实现。以下代码最能说明我的需求:

use core::marker::PhantomData;

// 生命周期`'a`仅对创建`Foo`的`Owner`有效
#[derive(Default, Copy, Clone)]
struct Foo<'a> {
    phantom: PhantomData<&'a Owner>,
}

#[derive(Default)]
struct Owner {}

impl Owner {
    fn get<'a, 'b: 'a>(&'a self) -> Foo<'b> {
        Foo::<'b>::default();
    }
    
    // 要求`self`的唯一生命周期同时适用于`Foo`
    fn set<'a>(&'a mut self, _foo: Foo<'a>) {
        // 任意代码
    }
}

fn main() {
    let mut owner = Owner::default();
    let mut owner2 = Owner::default();
    
    let anon_foo = Foo::default();
    let foo2 = owner2.get();
    
    owner.set(foo2); // 不应编译
    owner.set(anon_foo); // 也不应编译
    
    owner2.set(foo2); // 可以编译!
}

这是一个理论性问题,我并非要解决特定问题,只是想了解Rust中是否存在这类零开销抽象结构。

编辑补充:不要求在复杂场景下工作,只需做到安全失败(若编译器无法证明Owner是实际归属者,则绝不允许调用set)。此外,Owner结构体可通过宏创建,无需支持运行时构造。


可行的零开销实现方案

Rust的类型系统和生命周期系统完全可以实现这种需求,以下是两种符合要求的方案:

方案1:用generativity crate实现实例级归属追踪

generativity crate可以为每个Owner实例生成唯一、无法伪造的生命周期,让Foo绑定到该生命周期,从而确保只有创建它的Owner能接收它。该方案是实例级的,所有Owner是同一类型的不同实例,且完全零开销(PhantomData和Guard都是零大小类型)。

use generativity::make_guard;
use core::marker::PhantomData;

// Foo绑定到唯一的生成性生命周期'_id
#[derive(Default, Copy, Clone)]
struct Foo<'id> {
    _phantom: PhantomData<&'id ()>,
}

struct Owner<'id> {
    _guard: generativity::Guard<'id>,
}

impl<'id> Owner<'id> {
    // 创建Owner时生成唯一生命周期
    fn new() -> Self {
        make_guard!(guard);
        Owner { _guard: guard }
    }

    // 返回绑定当前Owner生命周期的Foo
    fn get(&self) -> Foo<'id> {
        Foo::default()
    }

    // 仅接受与当前Owner同生命周期的Foo
    fn set(&mut self, _foo: Foo<'id>) {
        // 业务逻辑
    }
}

fn main() {
    let mut owner = Owner::new();
    let mut owner2 = Owner::new();
    
    // 无法直接创建匿名Foo,因为没有对应的唯一生命周期
    // let anon_foo = Foo::default(); // 编译错误
    
    let foo2 = owner2.get();
    
    owner.set(foo2); // 编译错误:生命周期不匹配
    // owner.set(anon_foo); // 编译错误:无法创建
    
    owner2.set(foo2); // 编译通过!
}

方案2:用宏生成类型级归属标记

如果不想依赖外部crate,可以用宏为每个Owner生成唯一的标记类型,让Foo与该标记绑定。该方案是类型级的,不同的Owner是不同类型,但同样零开销,且符合你允许宏创建Owner的要求。

use core::marker::PhantomData;

// 宏生成带唯一标记的Owner和对应Foo
macro_rules! make_owner {
    ($owner_name:ident, $foo_name:ident) => {
        #[derive(Default)]
        pub struct $owner_name;
        
        #[derive(Default, Copy, Clone)]
        pub struct $foo_name {
            _phantom: PhantomData<&'static $owner_name>,
        }
        
        impl $owner_name {
            pub fn get(&self) -> $foo_name {
                $foo_name::default()
            }
            
            pub fn set(&mut self, _foo: $foo_name) {
                // 业务逻辑
            }
        }
    };
}

// 生成两个独立的Owner-Foo对
make_owner!(Owner1, Foo1);
make_owner!(Owner2, Foo2);

fn main() {
    let mut owner = Owner1::default();
    let mut owner2 = Owner2::default();
    
    let foo2 = owner2.get();
    
    owner.set(foo2); // 编译错误:类型不匹配(Foo2不能传给Owner1的set)
    // let anon_foo = Foo1::default(); // 可创建,但只能给Owner1使用
    
    owner2.set(foo2); // 编译通过!
}

总结

两种方案都满足你的需求:

  • 零开销:编译后无额外运行时成本
  • 安全失败:编译器严格验证归属关系,不符合条件的调用直接编译报错
  • 适配宏创建Owner的要求

其中方案1是实例级追踪,方案2是类型级追踪,可根据实际偏好选择。

内容的提问来源于stack exchange,提问作者Kryštof Vosyka

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.14 06:47:50