微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

如何使用易出错的初始化程序延迟初始化/填充`Option`?

如何解决如何使用易出错的初始化程序延迟初始化/填充`Option`?

假设我有一个包含 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(),而是要避免整体可读性。如果替代方案更加复杂和间接,我宁愿坚持这个。


完整示例代码 (on 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 没有例如try_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,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(())
}

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。