使用特定生命周期构建对象

时间:2014-01-14 15:48:02

标签: reference rust lifetime

我遇到了一个问题,就所有权,生命周期以及所有问题而言,我都不知道如何模拟Rust。

我有一个大结构:

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

结构很大,我想避免复制,因此我随身携带指针。

我有一个foo函数,它指向LargeStructf32值。如果此值大于字段x,我想创建一个x设置为此值的新对象,并返回它。如果它不大,那么我想返回原始指针本身。

我天真地实现了这样:

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

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

    &LargeStruct {

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

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

    ~LargeStruct {

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

  

if和else有不兼容的类型:期望~LargeStruct但找到了   &LargeStruct(预期&amp; -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);
}

2 个答案:

答案 0 :(得分:2)

您的方法背后的意图是,只有在我理解正确的情况下,才会在特定条件下返回修改后的副本。在Rust中,可以使用返回Option<~LargeStruct>类型的函数建模(甚至可能使用Option<LargeStruct>,但我不确定编译器是否可以在这种情况下有效地移动大对象。)

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

至于为什么你的方法不起作用:Rust不允许你返回对函数返回后将被释放的对象的引用。终身是一种说明对象必须至少与对它的引用一样长的方式。

答案 1 :(得分:2)

问题的答案

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

这可以通过enum指定返回类型作为对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获取&mut LargeStruct并修改结构,而不是在object.x < y时创建新结构。 (我认为这很可能是你真正想要的设计。)