修改Option内部的struct字段

时间:2015-08-28 12:44:17

标签: rust

我在更改选项内部结构的字段时遇到问题。这是代码:

struct MyStruct {
    field1 : i32,
    field2 : i32,
    // and many more...
}

impl MyStruct {
    pub fn field1(&mut self, field1 : i32) -> &mut Self {
        self.field1 = field1;
        self
    }
}

fn foo() -> Option<MyStruct> {
    None
}

fn bar() -> Option<MyStruct> {
    foo().as_mut().map(|s| s.field1(5))
}

fn main() {
    bar();
}

bar()的主要思想是从另一个返回Option<MyStruct>的函数中获取Option<MyStruct>,更改该结构的字段(如果结果不是None )并返回结果Option<MyStruct>

struct实现了构建器模式,所以我已经使用过了。

在这种情况下,我收到以下错误:

test.rs:18:5: 18:40 error: mismatched types:
expected `core::option::Option<MyStruct>`,
    found `core::option::Option<&mut MyStruct>`
(expected struct `MyStruct`,
    found &-ptr) [E0308]
test.rs:18     foo().as_mut().map(|s| s.field1(5))

我还尝试使用Option.take()并将构建器方法的签名更改为pub fn field1(mut self, field1 : i32) -> Self,但它们都不起作用。

如何使此代码有效?

我知道我可以创建另一个Option并将输入的每个字段映射到输出的相应字段,但MyStruct有很多字段,而且很繁琐。

4 个答案:

答案 0 :(得分:5)

根本没有理由使用as_mut()。由于您需要按值返回MyStruct,因此您只需map选项:

fn bar() -> Option<MyStruct> {
    foo().map(|mut s| { s.field1(5); s })
}

那就是它。您需要使用s标记mut,原因与let相同。

答案 1 :(得分:2)

修改 检查弗拉基米尔的答案,它会更好。

我们应该删除链接吗?

fn bar() -> Option<MyStruct> {
    let x: Option<MyStruct> = foo();
    let x: Option<&mut MyStruct> = x.as_mut();
    let x: Option<&mut MyStruct> = x.map(|s| s.field1(5));
    x
}

因此,删除绒毛后,问题是:

  

如何将Option<&mut MyStruct>转换为Option<MyStruct>

你不能按原样,借款规则禁止取得你只借的东西的所有权。

您有两种选择:

  • 制作克隆
  • 回到主人

让我们来说明一下:

// Requires that MyStruct implement Clone,
// use #[derive(Clone)] to auto-generate the implementation
fn bar_copy() -> Option<MyStruct> {
    foo().as_mut().map(|s| s.field1(5).clone())
}

fn bar_back() -> Option<MyStruct> {
    let mut my_struct = foo();
    my_struct.as_mut().map(|s| s.field1(5));
    my_struct
}

选择您喜欢的(我选择后者以避免复制)。

答案 2 :(得分:1)

TBH,我会以明显的方式做到这一点:

fn bar() -> Option<MyStruct> {
    let mut val = foo();
    if let Some(ref mut s) = val {
        s.field1(5);
    }
    val
}

它比使用map更长,但我发现使用map进行变异会让人觉得不舒服,我选择的显式变异也会产生更好的装配。

答案 3 :(得分:1)

由于其他人正在提出更广泛的建议,我指出你可以改变你的建设者&#34;按值使用self的语法。对于实际的构建器,这通常更符合人体工程学:

struct MyStruct {
    field1: i32,
    field2: i32,
}

impl MyStruct {
    pub fn field1(self, field1: i32) -> Self {
        MyStruct { field1: field1, ..self }
    }
}

fn foo() -> Option<MyStruct> {
    None
}

fn bar() -> Option<MyStruct> {
    foo().map(|s| s.field1(5))
}

fn main() {
    bar();
}

我还倾向于区分构建器和普通的链式方法调用。也就是说,构建器应该具有实际返回不同类型的build方法(或等效方法)。构建器通常是一种短命类型,因此按值传递很少会导致问题。