Время жизни Rust для структурных ссылок

Я только начал с Rust, но не могу понять, сколько жизней я могу решить, самостоятельно решив следующую проблему:

Этот тестовый проект о симуляции битов, позволяющих отслеживать его с помощью различных побитовых операций, например let newbit = oldbit1 ^ oldbit2 и глядя на newbit Я могу сказать, что после операции XOR с oldbit1 а также oldbit2 как операнды.

#[derive(Copy,Clone)]
pub enum TraceOperation {
        AND,
        OR,
        XOR,
        NOT,
}

#[derive(Copy,Clone)]
pub struct TraceBit<'a> {
        source_a: Option<&'a TraceBit<'a>>,
        source_b: Option<&'a TraceBit<'a>>,
        source_op: Option<TraceOperation>,
        value: bool,
}

Это компилируется, но я не до конца понимаю, почему параметры времени жизни так нужны. Я предполагаю, что компилятор не может ожидать, что члены source_a а также source_b жить до тех пор, пока сама структура не может быть верной, поэтому требуются явные времена жизни.

  • это предположение правильно?

Кроме того, я не до конца понимаю, почему мне нужно заново указывать параметр времени жизни для ссылочного типа, т.е. зачем мне писать source_a: Option<&'a TraceBit<'a>> в отличие от source_a: Option<&'a TraceBit>,

  • Для чего используется вторая жизнь? Как мне прочитать эту строку вслух? Я имею: "source_a переменная типа Option что может иметь Some ссылка (действительна, по крайней мере, до тех пор, пока сама структура и пока член source_b) к примеру TraceBit"

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

use std::ops::BitXor;
impl<'a> BitXor for TraceBit<'a> {
        type Output = Self;
        fn bitxor(self, rhs: Self) -> Self {
                let valA: usize = if self.value { 1 } else { 0 };
                let valB: usize = if rhs.value { 1 } else { 0 };
                let val = if valA ^ valB != 0 { true } else { false };
                TraceBit { source_a: Some(&self), source_b: Some(&rhs), source_op: Some(TraceOperation::XOR), value: val }
        }
}

Это в основном чистое предположение, основанное на документации BitXor. Итак, что я пытаюсь сделать в очень явной форме, это выполнить операцию xor над двумя входными переменными и создать новую TraceBit как вывод с входами, сохраненными в нем как ссылка.

error[E0597]: `self` does not live long enough
  --> libbittrace/src/lib.rs:37:30
   |
37 |   TraceBit { source_a: Some(&self), source_b: Some(&rhs), source_op: Some(TraceOperation::XOR), value: val }
   |                              ^^^^ does not live long enough
38 |  }
   |  - borrowed value only lives until here
   |
note: borrowed value must be valid for the lifetime 'a as defined on the impl at 31:1...
  --> libbittrace/src/lib.rs:31:1
   |
31 | / impl<'a> BitXor for TraceBit<'a> {
32 | |  type Output = Self;
33 | |  fn bitxor(self, rhs: Self) -> Self {
34 | |   let valA: usize = if self.value { 1 } else { 0 };
...  |
40 | |
41 | | }
   | |_^

error[E0597]: `rhs` does not live long enough
  --> libbittrace/src/lib.rs:37:53
   |
37 |   TraceBit { source_a: Some(&self), source_b: Some(&rhs), source_op: Some(TraceOperation::XOR), value: val }
   |                                                     ^^^ does not live long enough
38 |  }
   |  - borrowed value only lives until here
   |
note: borrowed value must be valid for the lifetime 'a as defined on the impl at 31:1...
  --> libbittrace/src/lib.rs:31:1
   |
31 | / impl<'a> BitXor for TraceBit<'a> {
32 | |  type Output = Self;
33 | |  fn bitxor(self, rhs: Self) -> Self {
34 | |   let valA: usize = if self.value { 1 } else { 0 };
...  |
40 | |
41 | | }
   | |_^

error: aborting due to 2 previous errors
  • Кажется, ничто не живет дольше, чем сама операция xor, но как я могу решить эту проблему?

Я пробовал различные обходные пути / изменения в коде, но безрезультатно, и в любом случае мне больше нравится понимать проблему, чем угадывать правильное решение....

2 ответа

Древовидные структуры должны использовать Box тип указателя (Option<Box<TraceBit>>). В целом, в структурах вы должны предпочитать собственные типы.

Ссылки на Rust - это не просто указатели. Это заимствования (блокировки чтения / записи во время компиляции) данных, которые должны существовать как принадлежащие где-то еще.

Так что если у вас есть собственная версия TraceBit:

pub struct TraceBit {
    source_a: Option<Box<TraceBit>>,
}

тогда ссылка на него имеет тип: &'a TraceBit, но ссылки на тип не изменяют внешний вид типа, поэтому тип source_a все еще Box<TraceBit>, Вы можете продолжать получать &'a TraceBit ссылки рекурсивно шаг за шагом:

trace_bit = trace_bit.source_a.as_ref().unwrap();

но в Rust нет конструкции, в которой взятие ссылки на корень дерева внезапно превращает все дерево в дерево ссылок, поэтому создаваемый вами тип не может существовать, и поэтому вы не можете получить правильные аннотации типов.

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

use std::rc::Rc;

#[derive(Debug)]
pub enum TraceOperation {
    AND,
    OR,
    XOR,
    NOT,
}

#[derive(Debug)]
pub enum BitName<T> {
    Name(Rc<T>),
    Combination(Rc<(TraceOperation, BitName<T>, BitName<T>)>),
}

impl<T> Clone for BitName<T> {
    fn clone(&self) -> Self {
        match self {
            &BitName::Name(ref x) => BitName::Name(Rc::clone(x)),
            &BitName::Combination(ref x) => BitName::Combination(Rc::clone(x)),

        }
    }
}

impl<T> From<T> for BitName<T> {
    fn from(x:T) -> Self {
        BitName::Name(Rc::new(x))
    }
}

impl<T> BitName<T> {
    pub fn combine(op : TraceOperation, a : &Self, b :&Self) -> Self {
        BitName::Combination(Rc::new((op, (*a).clone(), (*b).clone())))
    }
}

fn main() {
    let x : BitName<String> = BitName::from(String::from("x"));
    let y : BitName<String> = BitName::from(String::from("y"));
    let xandy = BitName::combine(TraceOperation::AND, &x, &y);
    println!("{:?}", xandy);
}
Другие вопросы по тегам