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

标签 rust option-type lazy-initialization

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

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

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()方法。

其他解决方法同样冗长。但是,您可以使用 match get_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(())
}

关于rust - 如何使用易出错的初始化程序延迟初始化/填充 `Option`?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/65751826/

相关文章:

multithreading - 如何将对堆栈变量的引用传递给线程?

testing - 如何将测试移动到 Rust 的 Cargo 二进制文件的单独文件中?

arrays - 如何连接已知长度的数组?

ios - Parse + Swift ft. 选项的问题

c# - Lazy<T> 与标准惰性实例化相比有哪些优势?

rust - 如何从 Rust 中的 C 联合访问值?

ios - init! 的目的是什么?失败的初始化器?

java - 延迟初始化完整性检查 : Variable might not have been initialized

c# - 此 MSDN 示例中 Lazy<T> 的用途

java - 连接可选列表