Многократное использование кода

Здравствуйте. Хочу сразу же извиниться, я понимаю что для кого-то ответ на мой вопрос покажется очевидным. Но я не могу понять, как мне избежать многократного использования кода в функции “value”? Я пытаюсь использовать замыкания, но ничего из этого не выходит, так как владение “self” передаётся в замыкание и “match” выдаёт ошибку.

struct Cacher<T,U>
    where T: Fn(U) -> U
{
    calculation: T,
    argument: Option<U>,
    value: Option<U>,
}

impl<T,U> Cacher<T,U>
    where T: Fn(U) -> U, U: Copy + PartialEq
{
    fn new(calculation: T) -> Cacher<T,U> {
        Cacher {
            calculation,
            argument: None,
            value: None,
        }
    }

    fn value(&mut self, arg: U) -> U {

        /*let mut f = |a: U| -> U {
            let v = (self.calculation)(a);
            self.argument = Some(a);
            self.value = Some(v);
            v
        };*/

        match self.value {
            Some(v) => {
                if self.argument.unwrap() == arg { v }
                else {
                    let v = (self.calculation)(arg);
                    self.argument = Some(arg);
                    self.value = Some(v);
                    v
                    //f(arg)
                }
            },
            None => {
                let v = (self.calculation)(arg);
                self.argument = Some(arg);
                self.value = Some(v);
                v
            },
        }
    }
}

Вариант с выносом общего кода в обычный метод не годится?

// ...
    fn value(&mut self, arg: U) -> U {
        if let Some(v) = self.value {
            if self.argument.unwrap() == arg {
                v
            } else {
                self.f(arg)
            }
        } else {
            self.f(arg)
        }
    }

    fn f(&mut self, arg: U) -> U {
        let v = (self.calculation)(arg);
        self.argument = Some(arg);
        self.value = Some(v);
        v
    }
// ...

Playground

1 лайк

Спасибо, это подходящий вариант. Я думал что есть какой либо другой способ, сделать это в пределах одной функции. Если кто нибудь знает такой, прошу напишите, очень интересно.

Что ж, во первых, само сопоставление можно упростить и просто схлопнуть две ветки, тогда ничего никуда выносить вообще не обязательно:

    fn value(&mut self, arg: U) -> U {
        match self.value {
            Some(v) if self.argument.unwrap() == arg => v,
            _ => {
                let v = (self.calculation)(arg);
                self.argument = Some(arg);
                self.value = Some(v);
                v
            }
        }
    }

Или можно таки вмазаться замыканием, но, кмк, в этой ситуации проще просто явно в него self передавать только на время вызова, а не пытаться его захватить на все время жизни замыкания и мучаться с конфликтами ВЖ:

    fn value(&mut self, arg: U) -> U {
        let f = |this: &mut Self, a: U| -> U {
            let v = (this.calculation)(a);
            this.argument = Some(a);
            this.value = Some(v);
            v
        };

        if let Some(v) = self.value {
            if self.argument.unwrap() == arg {
                v
            } else {
                f(self, arg)
            }
        } else {
            f(self, arg)
        }
    }
1 лайк

Супер, кажется это то что нужно. Понял свою ошибку. Огромное спасибо.

Я бы ещё отметил, что self.argument.unwrap() во всех реализациях мягко намекает, что более идиоматично было бы хранить не argument: Option<U> и value: Option<U>, которые всегда обязаны быть либо оба Some, либо оба None, а cache: Option<(U, U)>, который бы это гарантировал автоматически.