Я новичок в 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
Вы близки здесь, но у вас есть две проблемы.
Во-первых, 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})
}
}
@ChayimFriedman На самом деле для этого, вероятно, потребуется take_mut()
.
Вы также можете заставить это работать с split_at_mut()
, с mem::take()
, а затем переназначить.
@ChayimFriedman Да, я всегда забываю об этой технике. Похоже, именно так и реализован take_mut()
.
@ChayimFriedman Я переписал свой ответ, чтобы он лучше соответствовал API OP, который ищет.
Я даже не знал, что сама ссылка может быть изменена вместе с данными, стоящими за ней. Исходя из опыта работы с Python, Rust — это большой отход. Используя ваш лучший пример, я смог заставить свой код работать, спасибо.
@DeathDonkey387 Без проблем. Обратите внимание, что нижний пример работает точно так же, но не требует использования ночного Rust с экспериментальными функциями.
Я не совсем понимаю последствия нахождения в среде 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
}
}
Лучше, чем вернуть хвост, это взять
&mut &'a mut [u8]
. Это также лучше работает сtake_mut()
.