rust - 如何在函数中借用 "take"切片的第一部分?

标签 rust

背景

我对 Rust 相当陌生,我正在尝试编写一个数据包解析器。它位于没有堆的 #[no_std] 环境中。

解析器的输入是对字节切片的可变引用 (&mut [u8]),输出是数据包头的 heapless::Vec结构(实际上每个只是一个结构,带有对其切片的引用和一些用于获取 header 字段的函数)。

问题

我正在努力将初始提供的切片拆分为各个数据包 header 所需的较小切片。在函数中借用切片并使用 slice.take_mut() 获取第一 block 似乎不会返回切片其余部分的权限,因此对该函数的第二次调用无法编译。

我还尝试过使用 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})
    }
}

( Playground )

请注意,您可以使用 split_at_mut 来在稳定的 Rust 上实现此操作。这需要一些涉及 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})
    }
}

( Playground )

关于rust - 如何在函数中借用 "take"切片的第一部分?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/76003226/

相关文章:

rust - 可以是 `Send` 的 RefCell 替代方案?

c - 使用类型 `[my_struct]` 是将 C 结构数组传递给 Rust 函数的正确方法吗?

rust - 哪个对堆分配点的引用是正确的?

rust - 为什么编译器会为特征及其实现推断出不同的生命周期?

reference - Vec<PathBuf> 到 &[&Path] 没有分配?

rust - 如何使用同级/相邻 Rust 文件中的结构?

rust - 如何从特征对象中获取对具体类型的引用?

rust - 当 Foobar 只有一个字段时,直接反序列化一个 Vec<Foobar<T>> 为 Vec<T>

rust - 写入带有偏移量的静态大小文件

casting - 我是否过多地转换了整数类型?