【问题标题】:What is the difference between "context" and "with_context" in anyhow?无论如何,“context”和“with_context”有什么区别?
【发布时间】:2021-04-04 04:22:43
【问题描述】:

这是无论如何Context的文档:

/// Wrap the error value with additional context.
fn context<C>(self, context: C) -> Result<T, Error>
where
    C: Display + Send + Sync + 'static; 
/// Wrap the error value with additional context that is evaluated lazily
/// only once an error does occur.
fn with_context<C, F>(self, f: F) -> Result<T, Error>
where
    C: Display + Send + Sync + 'static,
    F: FnOnce() -> C;

实际上,区别在于with_context 需要一个闭包,如anyhow 的README 所示:

use anyhow::{Context, Result};

fn main() -> Result<()> {
    // ...
    it.detach().context("Failed to detach the important thing")?;

    let content = std::fs::read(path)
        .with_context(|| format!("Failed to read instrs from {}", path))?;
    // ...
}

但看起来我可以用context替换with_context方法,通过删除||摆脱闭包,程序的行为不会改变。

这两种方法在底层有什么区别?

【问题讨论】:

  • 如果你看一下源码,和docs.rs/anyhow/1.0.36/src/anyhow/context.rs.html#42-60一模一样
  • 我明白了。那么在运行时有什么区别吗?我没有看到为相同的确切功能提供两个功能的好处。
  • 不同之处在于,即使 Result 不是 Err,一个也会评估您可能非常复杂的上下文,而另一个仅在实际需要该上下文时调用创建上下文的闭包。这正是with_content 的文档提到的内容:使用附加上下文包装错误值,该上下文仅在发生错误时延迟评估

标签: methods error-handling rust closures lazy-evaluation


【解决方案1】:

正如anyhow::Context::with_context 的文档所述:

使用附加上下文包装错误值,仅在发生错误时惰性评估。

如果传递给context 的内容在计算上可能很昂贵,最好使用with_context,因为只有在调用with_context 时才会评估传递的闭包。这被称为以 lazy 而不是 eager 的方式进行评估。

标准库中存在类似行为,例如:

【讨论】:

    【解决方案2】:

    提供给with_context 的闭包是延迟评估的,而您使用with_context 而不是context 的原因与您选择延迟评估任何东西的原因相同:它很少发生并且计算成本很高。一旦满足这些条件,with_context 就比context 更可取。注释伪示例:

    fn calculate_expensive_context() -> Result<()> {
        // really expensive
        std::thread::sleep(std::time::Duration::from_secs(1));
        todo!()
    }
    
    // eagerly evaluated expensive context
    // this function ALWAYS takes 1+ seconds to execute
    // consistently terrible performance
    fn failable_operation_eager_context(some_struct: Struct) -> Result<()> {
        some_struct
            .some_failable_action()
            .context(calculate_expensive_context())
    }
    
    // lazily evaluated expensive context
    // function returns instantly, only takes 1+ seconds on failure
    // great performance for average case, only terrible performance on error cases
    fn failable_operation_lazy_context(some_struct: Struct) -> Result<()> {
        some_struct
            .some_failable_action()
            .with_context(|| calculate_expensive_context())
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2011-02-02
      • 1970-01-01
      • 1970-01-01
      • 2021-09-11
      • 2012-04-19
      • 2015-02-15
      相关资源
      最近更新 更多