Реализация добавления признака для перечисления без перемещения данных

#rust

Вопрос:

Я пытаюсь реализовать рекурсивное перечисление для чего-то вроде дерева синтаксического анализа. В настоящее время моя отправная точка выглядит следующим образом:

 #[derive(Debug)]
enum Value<T: Add   Copy> {
    Sum(Rc<Value<T>>, Rc<Value<T>>),
    Scalar(T)
}
 

Очевидно, что сейчас я забочусь только о добавлении и добавлю другие операции позже. Я хочу реализовать эту Add особенность, чтобы обеспечить следующее:

 let x = Value<f32>::Scalar(0.3);
let y = Value<f32>::Scalar(0.3);
let z = x   y;
 

z теперь должно быть Value<f32>::Sum и x и y не должно быть перемещено. Мой вопрос в том, как реализовать эту Add черту. Или, скорее, для каких типов. Мой первый подход выглядел так:

 impl<T: Add   Copy> Add for crate::Value<T> {
    type Output = Self;

    fn add(self, other: Value<T>) -> Self {
        ...
    }
}
 

Это решение компилируется, но данные перемещаются. Если я попытаюсь изменить параметры add метода на ссылки, это не удастся, потому что подпись не соответствует ожидаемой. Имеет смысл, поэтому я попытался реализовать эту черту для amp;Value<T> . Кажется, это работает, но я x y больше не могу писать, потому что это не ссылки. Я также боролся с типом возврата, потому что этого не может быть amp;Value<T> , но должно быть Value<T> .

Я выбрал другой подход, чтобы полностью работать с Rc<Value<T>> ним, но мне не разрешено его реализовывать Add Rc<...> . Я знаю, что есть одна AsRef особенность, но я совсем новичок в Rust и теперь имею представление о том, как она будет вписываться в мой вариант использования.

Любой намек на то, как собрать эти части воедино и заставить их работать, был бы очень признателен.

Ответ №1:

Вы были почти на месте, вы могли бы сохранить свой подход, Rc но обернуть его в свой собственный тип:

 use std::rc::Rc;
use std::ops::Add;

#[derive(Debug, Clone)]
struct WrapperValue<T: Add   Copy>(Rc<Value<T>>);

#[derive(Debug)]
enum Value<T: Add   Copy> {
    Sum(WrapperValue<T>, WrapperValue<T>),
    Scalar(T)
}

impl<T: Add   Copy> Add for WrapperValue<T> {
    type Output = WrapperValue<T>;

    fn add(self, other: WrapperValue<T>) -> Self {
        WrapperValue(Rc::new(Value::Sum(self.clone(), other.clone())))
    }
}
 

Игровая площадка

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

 impl<T: Add   Copy> Add for amp;WrapperValue<T> {
    type Output = WrapperValue<T>;

    fn add(self, other: amp;WrapperValue<T>) -> Self::Output {
        WrapperValue(Rc::new(Value::Sum(self.clone(), other.clone())))
    }
}
 

Игровая площадка

Комментарии:

1. Это компилируется, но если я создам два экземпляра WrapperValue и добавлю их через» «, данные все равно будут перемещены.

2. Но теперь ты должен написать amp;x amp;y . Я хотел писать x y , а не перемещать данные. Я начинаю сомневаться, что это возможно. Может работать с типами, которые реализуют Copy , но это невозможно Rc .

3. @Achim afaik ты не можешь, это из-за границ самой черты. вам придется выбрать, я думаю 🙁