如何层次化组织昂贵且依赖关系计算,以进行延迟评估?

huangapple go评论57阅读模式
英文:

How to hierarchically organize expensive and dependant calculations that evaluate lazily?

问题

以下是翻译好的代码部分:

struct Root {
    cheap: i32,
    expensive1: Option<Expensive1>,
}
impl Root {
    pub fn new() {
        Root {cheap: 0, expensive1: None}
    }
    pub fn get_expensive_1(&self) -> &Expensive1 {
        if self.expensive1.is_none() {
            self.expensive1 = Some(Expensive1 {
                value: 1,
                expensive1_1: None,
                expensive1_2: None,
            });
        };
        &self.expensive1.as_ref().unwrap()
    }
}

struct Expensive1 {
    value: i32,
    expensive1_1: Option<Expensive1x1>,
    expensive1_2: Option<Expensive1x2>,
}
impl Expensive1 {
    pub fn get_expensive1_1(&self) -> &Expensive1x1 {}
    pub fn get_expensive1_2(&self) -> &Expensive1x2 {}
}

struct ExpensiveN {
    value: i32
}

请注意,代码中已进行了一些修复,以便正确构建对象和返回引用。然而,由于Rust的所有权系统,仍然可能需要进一步处理所有权和借用的问题,具体取决于实际用例和代码的上下文。如果您需要更多帮助来解决所有权和借用问题,请提供更多详细信息。

英文:

I would like to construct something along the lines of the code below, which is an attempt at describing multiple related values which are expensive to compute. The computations are deterministic and tied to the value of Root, although I'd prefer to compute them only when necessary.

struct Root {
    cheap: i32,
    expensive1: Option&lt;Expensive1&gt;,
}
impl Root {
    pub fn new() {
        Root {cheap: 0, expensive1: None}
    }
    pub fn get_expensive_1(&amp;self) -&gt; &amp;Expensive1{
        if self.expensive1.is_none() {
            self.expensive1 = Expensive1 {
                value: 1,
                expensive1_1: None,
                expensive1_2: None,
            }
        };
        &amp;self.expensive1
    }
}

struct Expensive1 {
    value: i32,
    expensive1_1: Option&lt;Expensive1x1&gt;,
    expensive1_2: Option&lt;Expensive1x2&gt;,
}
impl Expensive1 {
    pub fn get_expensive1_1() -&gt; &amp;Expensive1x1 {}
    pub fn get_expensive1_2() -&gt; &amp;Expensive1x2 {}
}

struct ExpensiveN {
    value: i32
}

It seems only Root should have a new method, as the other values should be reached by some path starting with a Root value. As such, was aiming for an API similar to the one below.

let root = Root::new();
let deep = root.get_expensive1().get_expensive1_1().get_expensive1_1_1().get_expensive_n();

I've been able to implement all the methods on their own, although I'm having a horrendous time with ownership when trying establish a hierarchichal relationship between values as described above. Each getter is trying to mutate data when it's not available while returning a reference to it.

I've been playing around with Rc, RefCell, Box, get_or_insert_with, and various combinations of &amp;self/&amp;mut self, yet haven't been able to crack this one. Most of the errors encountered were not being able to mutate unmutable references or trying to return values from the getters error[E0515]: cannot return reference to temporary value.

Any suggestions on how to implement this?

答案1

得分: 3

你应该使用OnceCell来自once-cell crate(即将在1.70版本中稳定地加入标准库)。它以一种内部可变性的方式工作,只能设置一次(非常适合懒惰求值),但一旦设置,就可以提供对其内部值的引用。

这是第一层的示例:

use once_cell::unsync::OnceCell;

struct Root {
    cheap: i32,
    expensive1: OnceCell<Expensive1>,
}

impl Root {
    pub fn new() -> Root {
        Root {
            cheap: 0,
            expensive1: OnceCell::new(),
        }
    }

    pub fn get_expensive_1(&self) -> &Expensive1 {
        self.expensive1.get_or_init(|| {
            // 昂贵的计算
            Expensive1 {
                value: 1,
                expensive1_1: OnceCell::new(),
                expensive1_2: OnceCell::new(),
            }
        })
    }
}

更多信息在播放场

英文:

You should use a OnceCell from the once-cell crate (soon to be stabilized into the standard library in the 1.70 release). It uses interior mutability in a way that it can only be set once (which is good for lazy evaluation) but can provide references to its inner value once set.

Here's how that'd look on the first layer:

use once_cell::unsync::OnceCell;

struct Root {
    cheap: i32,
    expensive1: OnceCell&lt;Expensive1&gt;,
}

impl Root {
    pub fn new() -&gt; Root {
        Root {
            cheap: 0,
            expensive1: OnceCell::new(),
        }
    }

    pub fn get_expensive_1(&amp;self) -&gt; &amp;Expensive1 {
        self.expensive1.get_or_init(|| {
            // expensive evaluation
            Expensive1 {
                value: 1,
                expensive1_1: OnceCell::new(),
                expensive1_2: OnceCell::new(),
            }
        })
    }
}

More on the playground.

huangapple
  • 本文由 发表于 2023年5月15日 02:35:52
  • 转载请务必保留本文链接:https://go.coder-hub.com/76249104.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定