我该如何懒惰地初始化/填充一个容易出错的初始化器的“选项”?

凯文·里德(Kevin Reid)

假设我有一个包含的结构Option<Resource>,其中Resource我需要处理的某种类型需要分配外部资源(在实际情况下为GPU内存),因此可能会失败。从一种方法中,如果尚未完成分配,我想尝试分配,但是如果分配失败,则传播或处理该错误。

如果不是失败案例,那Option::get_or_insert_with将是完美的。实际上,我想到的最简洁的解决方案涉及到unwrap(),这是不雅的,因为它看起来像是潜在的恐慌症:

struct Container {
    resource: Option<Resource>,
    ...
}

impl Container {
    ...
    fn activate(&mut self) -> Result<(), Error> {
        if self.resource.is_none() {
            self.resource = Some(Resource::new()?);
        }
        let resource: &mut Resource = self.resource.as_mut().unwrap();
        // ... now do things with `resource` ...
        Ok(())
    }
    ...
}

有没有一种方法可以初始化Option得比这少呢?需要明确的是,我不只是在寻找避免unwrap(),而且整体可读性。如果替代方案更加复杂和间接,我宁愿坚持这一点。


完整的示例代码(在Rust Playground上):

#[derive(Debug)]
struct Resource {}
#[derive(Debug)]
struct Error;

impl Resource {
    fn new() -> Result<Self, Error> {
        Ok(Resource {})
    }

    fn write(&mut self) {}
}

#[derive(Debug)]
struct Container {
    resource: Option<Resource>,
}

impl Container {
    fn new() -> Self {
        Self { resource: None }
    }

    fn activate(&mut self) -> Result<(), Error> {
        if self.resource.is_none() {
            self.resource = Some(Resource::new()?);
        }
        self.resource.as_mut().unwrap().write();
        Ok(())
    }
}

fn main() {
    Container::new().activate();
}
瓦伦丁

确实,get_or_insert_with()但是返回Result<&mut T, E>是您可以用来简化代码的东西。但是,您已经发现Option没有egtry_get_or_insert_with()方法。

其他解决方法也将同样冗长但是,可以使用matchget_or_insert()来避免unwrap()这样的情况:

fn activate(&mut self) -> Result<(), Error> {
    let res = match &mut self.resource {
        Some(res) => res,
        None => self.resource.get_or_insert(Resource::new()?),
    };
    res.write();

    Ok(())
}

如果经常使用该方法,那么您还可以定义自己的try_get_or_insert_with()trait方法,并将其实现为Option<T>

trait TryGetOrInsert<T> {
    fn try_get_or_insert_with<E, F>(&mut self, f: F) -> Result<&mut T, E>
    where
        F: FnOnce() -> Result<T, E>;
}

impl<T> TryGetOrInsert<T> for Option<T> {
    fn try_get_or_insert_with<E, F>(&mut self, f: F) -> Result<&mut T, E>
    where
        F: FnOnce() -> Result<T, E>,
    {
        match self {
            Some(value) => Ok(value),
            None => Ok(self.get_or_insert(f()?)),
        }
    }
}

然后,您可以将activate()方法简化为以下内容:

fn activate(&mut self) -> Result<(), Error> {
    let res = self.resource.try_get_or_insert_with(|| Resource::new())?;
    res.write();

    Ok(())
}

本文收集自互联网,转载请注明来源。

如有侵权,请联系 [email protected] 删除。

编辑于
0

我来说两句

0 条评论
登录 后参与评论

相关文章

如何初始化一个懒惰列表?

在Swift中重新初始化一个懒惰的初始化变量

我如何传递一个初始化的对象

如何初始化一个迭代器

用Collections.emptyList()声明后,懒惰地重新初始化一个可修改的列表

用括号括起来的初始化器列表初始化一个类

我应该初始化一个cv :: Mat

如何修改我的代码以在外部打印一个变量,该变量是在 try-catch 内部初始化的?

如何初始化一个类?

如何重新初始化一个Spring bean?

如何初始化一个空的 ComponentRef?

如何检查一个类是否被初始化?

如何初始化一个数组对象?

如何初始化一个数组行?

如何初始化一个LinkedList?

如何初始化一个空的结构?

如何创建一个初始化变量的算法?

如何初始化一个让文本属性

如何初始化一个 bouncycastle BigInteger?

如何初始化一个 ObservableList?

如何在Rails中访问在一个初始化器文件中定义的常量到另一个初始化器文件中?

让编译器初始化一个数组或手动遍历该数组进行初始化是否更快?

在Swift中,如何为一个类创建一个便利的初始化,在该实现中初始化的实现创建类值而不是调用现有的初始化

如何确保仅初始化一个实例,但从另一个类初始化?

我如何在C中初始化一个二维数组

如何自动为Swift类创建一个初始化器?

“便利初始化程序缺少对另一个初始化程序的'自我'调用”

"不可为空的实例字段 '_items' 必须被初始化。\n尝试添加一个初始化器

为SKScene创建一个自定义的初始化器,以覆盖便捷初始化吗?(fileNamed :)