使用 Option<&T> 获取 Option::unwrap_or_else 行为的简洁方法

Dav*_*lak 3 rust

我想知道是否有任何优雅的解决方案可以获取类似于 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);
}
Run Code Online (Sandbox Code Playgroud)

当 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
Run Code Online (Sandbox Code Playgroud)

“Rust 惯用的”写作方式是什么use_or_default?除了使用一些便捷方法use_or_default_nicer创建泛型类型+之外,有没有一种方法可以让它看起来与实现方式类似?MaybeDefaultedRef<T>如果有更好的方法,我愿意重构整个事情。

rod*_*igo 5

你可以这样写:

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);
}
Run Code Online (Sandbox Code Playgroud)

也就是说,您可以将值本身保留在函数之外,然后在必要时分配给它。不幸的是, 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);
}
Run Code Online (Sandbox Code Playgroud)

在我看来,即使有点长,这也更好,因为您不需要可变Option<_>的变量maybe或假初始化。

match有些人在使用时会感到有点挫败Option,并认为这不符合习惯,但我并不特别在意。