使用 Option<&T> 获得 Option::unwrap_or_else 行为的简洁方法
Clean way to get Option::unwrap_or_else behaviour with an Option<&T>
我想知道是否有任何优雅的解决方案可以使 code/behaviour 类似于 Option<&T> 上的 unwrap_or_else。我的用例是将一个可选的引用传递给一个函数,如果它没有被使用,那么创建一个相同类型的默认值来使用。这是我的代码的简化版本:
#[derive(Debug)]
struct ExpensiveUnclonableThing {}
fn make_the_thing() -> ExpensiveUnclonableThing {
// making the thing is slow
// ...
ExpensiveUnclonableThing {}
}
fn use_the_thing(thing_ref: &ExpensiveUnclonableThing) {
dbg!(thing_ref);
}
fn use_or_default(thing_ref_opt: Option<&ExpensiveUnclonableThing>) {
enum MaybeDefaultedRef<'a> {
Passed(&'a ExpensiveUnclonableThing),
Defaulted(ExpensiveUnclonableThing),
}
let thing_md = match thing_ref_opt {
Some(thing_ref) => MaybeDefaultedRef::Passed(thing_ref),
None => MaybeDefaultedRef::Defaulted(make_the_thing()),
};
let thing_ref = match &thing_md {
MaybeDefaultedRef::Passed(thing) => thing,
MaybeDefaultedRef::Defaulted(thing) => thing,
};
use_the_thing(thing_ref);
}
fn use_or_default_nicer(thing_ref_opt: Option<&ExpensiveUnclonableThing>) {
let thing_ref = thing_ref_opt.unwrap_or_else(|| &make_the_thing());
use_the_thing(thing_ref);
}
fn main() {
let thing = make_the_thing();
use_or_default(Some(&thing));
use_or_default(None);
use_or_default_nicer(Some(&thing));
use_or_default_nicer(None);
}
当 unwrap_or_else 闭包结束时,东西立即被丢弃,所以我当然得到一个错误,说明我不能这样做:
error[E0515]: cannot return reference to temporary value
--> src/main.rs:31:53
|
31 | let thing_ref = thing_ref_opt.unwrap_or_else(|| &make_the_thing());
| ^----------------
| ||
| |temporary value created here
| returns a reference to data owned by the current function
'idiomatic Rust'的写法是什么use_or_default
?有没有办法让它看起来类似于 use_or_default_nicer
的实现方式,而不是通过使用一些方便的方法创建通用 MaybeDefaultedRef<T>
类型 +?如果有更好的方法,我愿意重构整个事情。
你可以这样写:
fn use_or_default_nicer(thing_ref_opt: Option<&ExpensiveUnclonableThing>) {
let mut maybe = None;
let thing_ref = thing_ref_opt.unwrap_or_else(
|| maybe.insert(make_the_thing())
);
use_the_thing(thing_ref);
}
也就是说,您可以将值本身保留在函数之外,然后在必要时分配给它。不幸的是,lambda 无法捕获单元化值,因此您必须创建变量 Option<ExpensiveUnclonableThing>
并使用 None
.
进行初始化
但是在我的真实代码中,我遇到了同样的问题,我写了一个手册match
:
fn use_or_default_nicer(thing_ref_opt: Option<&ExpensiveUnclonableThing>) {
let maybe;
let thing_ref = match thing_ref_opt {
Some(x) => x,
None => {
maybe = make_the_thing();
&maybe
}
};
use_the_thing(thing_ref);
}
在我看来,即使再长一点也更好,因为您不需要 Option<_>
或 maybe
可变变量或假初始化。
有些人在Option
上match
觉得有点失落,认为是un-idiomatic,但我并不特别在意。
一个普通的老if/else也可以,不用费心:
fn use_or_default_nicer(thing_ref_opt: Option<&ExpensiveUnclonableThing>) {
if let Some(e) = thing_ref_opt {
use_the_thing(e);
} else {
let e = make_the_thing();
use_the_thing(&e);
}
}
我想知道是否有任何优雅的解决方案可以使 code/behaviour 类似于 Option<&T> 上的 unwrap_or_else。我的用例是将一个可选的引用传递给一个函数,如果它没有被使用,那么创建一个相同类型的默认值来使用。这是我的代码的简化版本:
#[derive(Debug)]
struct ExpensiveUnclonableThing {}
fn make_the_thing() -> ExpensiveUnclonableThing {
// making the thing is slow
// ...
ExpensiveUnclonableThing {}
}
fn use_the_thing(thing_ref: &ExpensiveUnclonableThing) {
dbg!(thing_ref);
}
fn use_or_default(thing_ref_opt: Option<&ExpensiveUnclonableThing>) {
enum MaybeDefaultedRef<'a> {
Passed(&'a ExpensiveUnclonableThing),
Defaulted(ExpensiveUnclonableThing),
}
let thing_md = match thing_ref_opt {
Some(thing_ref) => MaybeDefaultedRef::Passed(thing_ref),
None => MaybeDefaultedRef::Defaulted(make_the_thing()),
};
let thing_ref = match &thing_md {
MaybeDefaultedRef::Passed(thing) => thing,
MaybeDefaultedRef::Defaulted(thing) => thing,
};
use_the_thing(thing_ref);
}
fn use_or_default_nicer(thing_ref_opt: Option<&ExpensiveUnclonableThing>) {
let thing_ref = thing_ref_opt.unwrap_or_else(|| &make_the_thing());
use_the_thing(thing_ref);
}
fn main() {
let thing = make_the_thing();
use_or_default(Some(&thing));
use_or_default(None);
use_or_default_nicer(Some(&thing));
use_or_default_nicer(None);
}
当 unwrap_or_else 闭包结束时,东西立即被丢弃,所以我当然得到一个错误,说明我不能这样做:
error[E0515]: cannot return reference to temporary value
--> src/main.rs:31:53
|
31 | let thing_ref = thing_ref_opt.unwrap_or_else(|| &make_the_thing());
| ^----------------
| ||
| |temporary value created here
| returns a reference to data owned by the current function
'idiomatic Rust'的写法是什么use_or_default
?有没有办法让它看起来类似于 use_or_default_nicer
的实现方式,而不是通过使用一些方便的方法创建通用 MaybeDefaultedRef<T>
类型 +?如果有更好的方法,我愿意重构整个事情。
你可以这样写:
fn use_or_default_nicer(thing_ref_opt: Option<&ExpensiveUnclonableThing>) {
let mut maybe = None;
let thing_ref = thing_ref_opt.unwrap_or_else(
|| maybe.insert(make_the_thing())
);
use_the_thing(thing_ref);
}
也就是说,您可以将值本身保留在函数之外,然后在必要时分配给它。不幸的是,lambda 无法捕获单元化值,因此您必须创建变量 Option<ExpensiveUnclonableThing>
并使用 None
.
但是在我的真实代码中,我遇到了同样的问题,我写了一个手册match
:
fn use_or_default_nicer(thing_ref_opt: Option<&ExpensiveUnclonableThing>) {
let maybe;
let thing_ref = match thing_ref_opt {
Some(x) => x,
None => {
maybe = make_the_thing();
&maybe
}
};
use_the_thing(thing_ref);
}
在我看来,即使再长一点也更好,因为您不需要 Option<_>
或 maybe
可变变量或假初始化。
有些人在Option
上match
觉得有点失落,认为是un-idiomatic,但我并不特别在意。
一个普通的老if/else也可以,不用费心:
fn use_or_default_nicer(thing_ref_opt: Option<&ExpensiveUnclonableThing>) {
if let Some(e) = thing_ref_opt {
use_the_thing(e);
} else {
let e = make_the_thing();
use_the_thing(&e);
}
}