Как правильно использовать объекты признаков, с возможностью изменения итерации по изменяемому контейнеру объектов признаков, при этом изменяя сам контейнер?

#rust

#Ржавчина

Вопрос:

Я пытаюсь написать анализатор пакетов, где в основном создается пакет путем анализа каждого слоя в пакете. Затем пакет содержит эти «слои» в векторе.

Код ~псевдокод ~ с ошибками компиляции выглядит примерно следующим образом —

Также добавлены комментарии ниже — для каждого шага. Я экспериментировал RefCell , но не смог заставить это работать. По сути, проблемы перечислены в конце кода.

Основной шаблон следующий — получить объект типа слоя (каждый тип слоя вернет default следующий объект на основе некоторого поля в текущем слое как «объект в штучной упаковке».)

Редактировать: я добавляю код, который больше, чем псевдокод — также добавлены следующие ошибки компиляции. Может быть, способ выяснить, как исправить эти ошибки, может решить проблемы.!

 
#[derive(Debug, Default)]
pub struct Packet<'a> {
    data: Option<amp;'a [u8]>,
    meta: PacketMetadata,
    layers: Vec<Box<dyn Layer<'a>>>,
}

pub trait Layer<'a>: Debug {
    fn from_u8<'b>(amp;mut self, bytes: amp;'b [u8]) -> Result<(Option<Box<dyn Layer>>, usize), Error>;
}

#[derive(Debug, Default)]
pub struct PacketMetadata {
    timestamp: Timestamp,
    inface: i8,
    len: u16,
    caplen: u16,
}

impl<'a> Packet<'a> {
    fn from_u8(bytes: amp;'a [u8], _encap: EncapType) -> Result<Self, Error> {
        let mut p = Packet::default();

        let eth = ethernet::Ethernet::default();

        let mut layer: RefCell<Box<dyn Layer>> = RefCell::new(Box::new(eth));
        let mut res: (Option<Box<dyn Layer>>, usize);
        let mut start = 0;
        loop {
            let mut decode_layer = layer.borrow_mut();

            // process it
            res = decode_layer.from_u8(amp;bytes[start..])?;

            if res.0.is_none() {
                break;
            }

            // if the layer exists, get it in a layer.
            let boxed = layer.replace(res.0.unwrap());
            start = res.1;

            // append the layer to layers.
            p.layers.push(boxed);
        }
        Ok(p)
    }
}

 

Ошибки компиляции

 error[E0515]: cannot return value referencing local variable `decode_layer`
  --> src/lib.rs:81:9
   |
68 |             res = decode_layer.from_u8(amp;bytes[start..])?;
   |                   ------------ `decode_layer` is borrowed here
...
81 |         Ok(p)
   |         ^^^^^ returns a value referencing data owned by the current function

error[E0515]: cannot return value referencing local variable `layer`
  --> src/lib.rs:81:9
   |
65 |             let mut decode_layer = layer.borrow_mut();
   |                                    ----- `layer` is borrowed here
...
81 |         Ok(p)
   |         ^^^^^ returns a value referencing data owned by the current function

error: aborting due to 2 previous errors; 3 warnings emitted

 

Непонятно, почему возникают вышеуказанные ошибки. Я использую значения, возвращаемые вызовами. (3: предупреждения, показанные выше, можно игнорировать, они являются неиспользуемыми предупреждениями.)

Проблемы —

  1. p.layers.last_mut и p.layers.push являются ли одновременные изменяемые заимствования недопустимыми. Я мог бы как-то поместить его за RefCell , но как это сделать, не ясно.
  2. Этот код похож по шаблону на syn::token::Token s, однако одно основное отличие заключается в том, что там Enum используется( TokenTree ) . В приведенном выше примере я не могу использовать Enum , потому что список поддерживаемых протоколов потенциально неограничен.
  3. Я не могу использовать Layer trait без Trait Objects из-за loop конструкции.
  4. Шаблон можно представить как — изменяемую итерацию по контейнеру объектов признаков при обновлении самого контейнера.
  5. Возможно, я упускаю что-то очень простое.

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

1. Почему вы назначаете срок службы для своей Layer черты, которую вы никогда не используете?

2. Ваш комментарий «Я не могу использовать Enum, потому что список поддерживаемых протоколов потенциально неограничен». Почему бы и нет? Вы можете установить enum non_exhaustive значение, позволяющее добавлять больше «протоколов» в будущем

3. @WBuck Я не думаю, что это означает OP. Я полагаю, что намерение состоит в том, что любой конечный пользователь может решить реализовать Layer признак и Packet должен работать с ним, что перечисление не может обработать.

4. В качестве последнего средства я бы добавил Enum . Но в идеале хотелось бы избежать этого. Что касается времени жизни слоя, это остаток от какого-то эксперимента, но не думаю, что это действительно проблема в отношении этих ошибок, которые я получаю. Но в конечном итоге очистил бы его.

5. На самом деле это время жизни Layer было проблемой!!!! :- (Аргх !!!.. Но это поможет понять, почему? 🙂 @Aplet123 не могли бы вы изменить свой комментарий на ответ? Я приму это как ответ!!

Ответ №1:

Проблема с приведенным выше кодом связана с пожизненной аннотацией на Layer черте. Если удалить эту пожизненную аннотацию, приведенный выше код действительно компилируется с несколькими изменениями, как указано ниже —

 // Layer Trait definition
pub trait Layer: Debug {
    fn from_u8(amp;mut self, bytes: amp;[u8]) -> Result<(Option<Box<dyn Layer>>, usize), Error>;
}


impl<'a> Packet<'a> {
    fn from_u8(bytes: amp;'a [u8], _encap: EncapType) -> Result<Self, Error> {
        let mut p = Packet::default();

        let eth = ethernet::Ethernet::default();

        let layer: RefCell<Box<dyn Layer>> = RefCell::new(Box::new(eth));
        let mut res: (Option<Box<dyn Layer>>, usize);
        let mut start = 0;
        loop {
            {
                // Do a `borrow_mut` in it's own scope, that gets dropped at the end.
                let mut decode_layer = layer.borrow_mut();
                res = decode_layer.from_u8(amp;bytes[start..])?;
            }

            if res.0.is_none() {
                // This is just required to push something to the RefCell, that will get dropped anyways.
                let fake_boxed = Box::new(FakeLayer {});
                let boxed = layer.replace(fake_boxed);

                p.layers.push(boxed);
                break;
            }

            // if the layer exists, get it in a layer.
            let boxed = layer.replace(res.0.unwrap());
            start = res.1;

            // append the layer to layers.
            p.layers.push(boxed);
        }
        Ok(p)
    }
}