我遇到了一个问题,就所有权,生命周期以及所有问题而言,我都不知道如何模拟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
,在第二种情况下,我返回&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);
}
答案 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)
}
}
然后,您需要在LargeStruct
和LargeStructRef
之间进行模式匹配 - 它不能对调用者透明。
你可以用不同的方式设计它来解决这些困难。例如,您可以让foo
获取&mut LargeStruct
并修改结构,而不是在object.x < y
时创建新结构。 (我认为这很可能是你真正想要的设计。)