【问题标题】:Build object with specific lifetime构建具有特定生命周期的对象
【发布时间】:2014-01-14 15:48:01
【问题描述】:

我有一个问题,我不知道如何在 Rust 中建模,涉及所有权、生命周期等等。

我有一个大结构:

struct LargeStruct {
    x: f32,
    n: i32,
    // lot of other fields.
}

结构很大,我想避免复制,因此我携带指向它的指针。

我有一个foo 函数,它接受一个指向LargeStruct 的指针和一个f32 值。如果此值大于字段x,我想创建一个将x 设置为此值的新对象,并将其返回。如果它不是更大,那么我想返回原来的指针本身。

我天真地这样实现它:

fn foo(object: &LargeStruct, y: f32) -> &LargeStruct {
    if object.x < y {
        LargeStruct {
            x: y,
            n: object.n,
            // ...
        }
    }
    else {
        object
    }
}

但它不起作用:if 的两个分支不返回相同的类型。在第一种情况下,我实际上返回了一个LargeStruct,而在第二种情况下,我返回了一个&amp;LargeStruct。如果我修改对象构造以获取其指针:

    &LargeStruct {

那么它也不起作用:构造的对象的生命周期太短,所以我无法从函数中返回它。

如果我尝试在堆上构建对象:

    ~LargeStruct {

我现在有另一个编译错误:

if 和 else 有不兼容的类型:预期为 ~LargeStruct 但发现 &amp;LargeStruct(应为 &-ptr,但找到 ~-ptr)

我尝试在函数签名中指定生命周期:

fn foo<'a>(object: &'a LargeStruct, y: f32) -> &'a LargeStruct {

但这无济于事:我不知道如何构建具有相同生命周期的新LargeStruct

我这样调用这个函数:

fn main() {
    let object = LargeStruct{ 
        x: 1.0, 
        n: 2,
        // ...
    };

    let result = foo(&object, 2.0);
}

【问题讨论】:

    标签: reference rust lifetime


    【解决方案1】:

    如果我理解正确,您的方法背后的意图是仅在某些条件下返回修改后的副本。在 Rust 中,这可以使用返回 Option&lt;~LargeStruct&gt; 类型的函数进行建模(甚至可能使用 Option&lt;LargeStruct&gt;,但我不确定在这种情况下编译器是否可以有效地移动大对象)。

    fn foo(object: &LargeStruct, y: f32) -> Option<~LargeStruct> {
        if object.x < y {
            return Some(~LargeStruct {
                x: y,
                //n: object.n,
                // ...
            })
        }
        None
    }
    

    至于你的方法为什么不起作用:Rust 不允许你返回对一旦函数返回就会被释放的对象的引用。生命周期是一种表示对象必须至少与对它的引用一样长的方式。

    【讨论】:

    • 好的,但是通过这种方法,当条件不满足时,您会返回 None,而不是像我打算的那样返回原始参考。我希望它对调用站点是透明的,以便它使用函数的结果而不需要知道哪个分支被击中。无论如何,感谢您对参考的解释。
    • 这可能是一个愚蠢的问题,但我不明白~LargeStruct 是什么。我用谷歌搜索了这个语法,但没有找到结果;我在操场上尝试了稳定版和夜间版,但编译器似乎不理解。这是什么意思? OP问题中有一个提示,这将在堆上分配LargeStruct,但这不是`Box`的作用吗?
    【解决方案2】:

    问题的答案

    用这种方式设计东西是不可能的,它完全透明;引用必须始终具有不超过其所有者范围的生命周期;因此不可能返回对范围不超过函数的对象的引用。

    这可以通过将返回类型指定为对 LargeStruct 或 LargeStruct 的引用的枚举来解决,但这很笨拙:

    pub struct LargeStruct {
        a: int,
    }
    
    pub enum LargeStructOrRef<'a> {
        LargeStructRef(&'a LargeStruct),
        LargeStruct(LargeStruct),
    }
    
    fn foo<'a>(object: &'a LargeStruct, y: f32) -> LargeStructOrRef<'a> {
        if object.x < y {
            LargeStruct(LargeStruct {
                x: y,
                n: object.n,
                // ...
            })
        } else {
            LargeStructRef(object)
        }
    }
    

    然后您需要在 LargeStructLargeStructRef 之间进行模式匹配——它不能对调用者透明。

    另类设计

    您可能可以用不同的方式设计它来解决这些困难。例如,您可以使foo 采用&amp;mut LargeStruct 并修改结构,而不是在object.x &lt; y 时创建新结构。 (我认为这很可能是您真正想要的设计。)

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2019-08-05
      • 1970-01-01
      • 1970-01-01
      • 2015-07-10
      • 1970-01-01
      • 2019-02-08
      • 1970-01-01
      • 2021-12-28
      相关资源
      最近更新 更多