我在更改选项内部结构的字段时遇到问题。这是代码:
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
有很多字段,而且很繁琐。
答案 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)
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
方法(或等效方法)。构建器通常是一种短命类型,因此按值传递很少会导致问题。