也许在struct方法中移动字段

时间:2016-05-06 20:22:48

标签: rust

我有以下对象:

#[derive(Debug)]
struct Bar(pub i32);

#[derive(Debug)]
struct Foo {
    pub bars: Vec<Bar>
}

假设我在拥有Foo的环境中,我可以移动该字段:

for x in foo.bars { /* ... */ } 

或者我可以通过参考

使用它
for x in &foo.bars { /* ... */ } 

但是,我想动态加载Vec<Bar>(例如,从数据库中加载),我可能会这样:

#[derive(Debug)]
struct Bar(pub i32);

#[derive(Debug)]
struct Foo {
    pub __bars: Option<Vec<Bar>>
}

impl Foo {    
    pub fn bars<'a>(&'a mut self) -> &'a Vec<Bar> {
        if self.__bars.is_none() {
            // load data here
            self.__bars = Some(Vec::new());
        }        
        self.__bars.as_ref().unwrap()        
    }    
}

但是现在我无法移动那个领域(即使我拥有该结构)。两个

for x in *foo.bars() { }

for x in foo.bars() { let y = *x; }

给我一​​个cannot move out of borrowed content错误。

我可以这样做:

impl Foo
    pub fn load_bars(&mut self) {
        if self.__bars.is_none() {
            // load data here
            self.__bars = Some(Vec::new());
        }
    }
}

let foo = Foo { /* ... */ };
foo.load_bars();
for x in foo.__bars.unwrap() { }

但这种情况更少&#34;愉快&#34;是否有一些特殊的引用类型,我可以从我的bars函数返回,这将允许我在以后需要时移动它(并且可以)?

1 个答案:

答案 0 :(得分:3)

  

是否有一些特殊的引用类型,我可以从我的bar函数返回,让我移动它

这两个概念是不兼容的。引用指向内存中的位置。移动值时,将更改其在内存中的位置,使任何引用无效。访问旧引用会导致内存不安全,因此Rust会阻止它。

我只是创建另一种解构类型的方法:

fn bars2(mut self) -> Vec<Bar> {
    self.bars();
    self.bars.expect("Impossible, we just ensured it was there")
}

然后,当您希望获得所有权时,您可以使用此方法:

fn main() {
    let mut foo = Foo { bars: None };
    for x in foo.bars() {
        // by ref
    }
    for x in foo.bars2() {
        // by value
    }
}

这个的情况下,这个工作原理是一样的,因为结构中只有一个字段。将值移入bars2并将向量移出self后,原始结构中没有任何内容可以获取!

如果您确实有多个值,则可以扩展该方法以返回包含每个值的元组:

fn bars2(mut self) -> (Vec<Bar>, String) {
    self.bars();
    let b = self.bars.expect("Impossible, we just ensured it was there");
    (b, self.owned_name)
}

或者如果您不关心许多其他值,您只需take一个值:

fn bars2(&mut self) -> Vec<Bar> {
    self.bars();
    self.bars.take().expect("Impossible, we just ensured it was there")
}

请注意,这只需要&mut self并将self.bars作为None。在其他情况下,可能会使用Optionmem::swapmem::replace

此外:

  1. 终身省略意味着您fn bars上不需要任何明确的生命周期。
  2. 最好返回&[T]而不是&Vec<T>,因为它会更好地隐藏内部实施。
  3. 不要使用前导下划线命名结构字段。一个前导下划线表示Rust程序员故意不使用该值,但这些非常清楚地使用。 Rust不是C ++,其中成员变量和方法位于同一名称空间中。