Как «взять» первую часть среза, заимствованного в функции?

Фон

Я новичок в Rust и пытаюсь написать анализатор пакетов. Он находится в среде #[no_std] без кучи.

Входные данные парсера — это изменяемая ссылка на слайс байтов (&mut [u8]), а выход — heapless::Vec структур заголовков пакетов (на самом деле каждая из них — это просто структура со ссылкой на свой слайс и некоторые функции для получения полей заголовка). .

Проблема

Я изо всех сил пытаюсь разделить первоначальный предоставленный фрагмент на более мелкие фрагменты, необходимые для отдельных заголовков пакетов. Заимствование фрагмента в функции и использование slice.take_mut() для получения первого фрагмента, по-видимому, не возвращает разрешения для остальной части фрагмента, поэтому второй вызов функции не компилируется.

Я также пробовал реализацию с использованием slice.split_at_mut() с похожими проблемами.

Мне не помогает то, что я изо всех сил пытаюсь понять жизни и то, как они вписываются во все это.

Пример

Я попытался сократить свой код до минимального примера, но мог упустить при этом какой-то важный контекст.

#![feature(slice_take)]

fn main() {
    let mut bytes: [u8; 30] = [
        1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
        11, 12, 13, 14, 15, 16, 17, 18, 19, 20,
        21, 22, 23, 24, 25, 26, 27, 28, 29, 30,
    ];
    let mut s1 = MyStruct::from_bytes(&mut bytes).unwrap();
    let mut s2 = MyStruct::from_bytes(&mut bytes).unwrap();
    println!("{:?}", s1);
    println!("{:?}", s2);
    println!("{:?}", bytes);
}

#[derive(Debug)]
struct MyStruct<'a> {
    data: &'a mut [u8],  // This should be a 10-byte slice.
}

impl<'a> MyStruct<'a> {
    fn from_bytes(mut data: &'a mut [u8]) -> Result<MyStruct<'a>, &'static str> {
        if data.len() < 10 {
            return Err("Need 10 bytes.");
        }
        let struct_data = data.take_mut(..10).unwrap();
        Ok(MyStruct{data: struct_data})
    }
}

Что я ожидал:

MyStruct { data: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] }
MyStruct { data: [11, 12, 13, 14, 15, 16, 17, 18, 19, 20] }
[21, 22, 23, 24, 25, 26, 27, 28, 29, 30]

Что происходит на самом деле:

error[E0499]: cannot borrow `bytes` as mutable more than once at a time
  --> src\main.rs:10:39
   |
9  |     let mut s1 = MyStruct::from_bytes(&mut bytes).unwrap();
   |                                       ---------- first mutable borrow occurs here
10 |     let mut s2 = MyStruct::from_bytes(&mut bytes).unwrap();
   |                                       ^^^^^^^^^^ second mutable borrow occurs here
11 |     println!("{:?}", s1);
   |                      -- first borrow later used here

Почему Python в конце концов умрет
Почему Python в конце концов умрет
Последние 20 лет были действительно хорошими для Python. Он прошел путь от "просто языка сценариев" до основного языка, используемого для написания...
2
0
60
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

Вы близки здесь, но у вас есть две проблемы.

Во-первых, from_bytes должна принимать изменяемую ссылку на изменяемый фрагмент. Это позволяет установить фрагмент, предоставленный вызывающим абонентом, в субрегион.

Во-вторых, вы передаете &mut bytes вместо фрагмента — вам нужно передать изменяемую ссылку на фрагмент, чтобы from_bytes мог настроить этот фрагмент так, чтобы он указывал на субрегион.

Устранение обеих этих проблем:

#![feature(slice_take)]

fn main() {
    let mut bytes: [u8; 30] = [
        1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
        11, 12, 13, 14, 15, 16, 17, 18, 19, 20,
        21, 22, 23, 24, 25, 26, 27, 28, 29, 30,
    ];
    let mut b = &mut bytes[..];
    let s1 = MyStruct::from_bytes(&mut b).unwrap();
    let s2 = MyStruct::from_bytes(&mut b).unwrap();
    println!("{:?}", s1);
    println!("{:?}", s2);
    println!("{:?}", b);
}

#[derive(Debug)]
struct MyStruct<'a> {
    data: &'a mut [u8],  // This should be a 10-byte slice.
}

impl<'a> MyStruct<'a> {
    fn from_bytes(data: &mut &'a mut [u8]) -> Result<MyStruct<'a>, &'static str> {
        if data.len() < 10 {
            return Err("Need 10 bytes.");
        }
        let struct_data = data.take_mut(..10).unwrap();
        Ok(MyStruct{data: struct_data})
    }
}

(Детская площадка)

Обратите внимание, что вы можете заставить это работать на стабильной версии Rust, используя вместо этого split_at_mut. Это требует некоторой гимнастики с участием std::mem::take() — на самом деле именно так take_mut реализовано под капотом!

fn main() {
    let mut bytes: [u8; 30] = [
        1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
        11, 12, 13, 14, 15, 16, 17, 18, 19, 20,
        21, 22, 23, 24, 25, 26, 27, 28, 29, 30,
    ];
    let mut b = &mut bytes[..];
    let s1 = MyStruct::from_bytes(&mut b).unwrap();
    let s2 = MyStruct::from_bytes(&mut b).unwrap();
    println!("{:?}", s1);
    println!("{:?}", s2);
    println!("{:?}", b);
}

#[derive(Debug)]
struct MyStruct<'a> {
    data: &'a mut [u8],  // This should be a 10-byte slice.
}

impl<'a> MyStruct<'a> {
    fn from_bytes(data: &mut &'a mut [u8]) -> Result<MyStruct<'a>, &'static str> {
        if data.len() < 10 {
            return Err("Need 10 bytes.");
        }
        let (struct_data, tail) = std::mem::take(data).split_at_mut(10);
        *data = tail;
        Ok(MyStruct{data: struct_data})
    }
}

(Детская площадка)

Лучше, чем вернуть хвост, это взять &mut &'a mut [u8]. Это также лучше работает с take_mut().

Chayim Friedman 13.04.2023 10:40

@ChayimFriedman На самом деле для этого, вероятно, потребуется take_mut().

cdhowie 13.04.2023 10:42

Вы также можете заставить это работать с split_at_mut(), с mem::take(), а затем переназначить.

Chayim Friedman 13.04.2023 10:43

@ChayimFriedman Да, я всегда забываю об этой технике. Похоже, именно так и реализован take_mut().

cdhowie 13.04.2023 10:46

@ChayimFriedman Я переписал свой ответ, чтобы он лучше соответствовал API OP, который ищет.

cdhowie 13.04.2023 10:50

Я даже не знал, что сама ссылка может быть изменена вместе с данными, стоящими за ней. Исходя из опыта работы с Python, Rust — это большой отход. Используя ваш лучший пример, я смог заставить свой код работать, спасибо.

DeathDonkey387 13.04.2023 13:07

@DeathDonkey387 Без проблем. Обратите внимание, что нижний пример работает точно так же, но не требует использования ночного Rust с экспериментальными функциями.

cdhowie 14.04.2023 00:03

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

#[derive(Debug)]
struct Parts<'a> {
    parts: Vec<MyStruct<'a>>,
    remain: Option<&'a mut [u8]>,
}

impl<'a> From<&'a mut [u8]> for Parts<'a> {
    fn from(mut value: &'a mut [u8]) -> Self {
        let mut parts = Parts {
            parts: Vec::new(),
            remain: None,
        };
        
        while !(value.len() < 10) {
            let struct_data = MyStruct{
                data: value.take_mut(..10).unwrap(),
            };
            parts.parts.push(struct_data);
        };
        
        if value.len() > 0 {
            parts.remain = value.take_mut(..value.len());
        }

        parts
    }
}

детская площадка

Другие вопросы по теме