rust 如何“获取”函数中借用的切片的第一部分?

lh80um4z  于 2023-04-21  发布在  其他
关注(0)|答案(2)|浏览(180)

背景

我是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
kb5ga3dv

kb5ga3dv1#

你离这里很近,但你有两个问题。
第一个是from_bytes需要获取一个可变切片的可变引用,这允许它将调用者提供的切片设置为一个子区域。
第二个是传入&mut bytes而不是slice --您需要传入一个slice的可变引用,以便from_bytes可以调整该slice以指向一个子区域。
解决这两个问题:

#![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
请注意,你可以在稳定的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})
    }
}

Playground

vkc1a9a2

vkc1a9a22#

我并不完全理解在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
    }
}

playground

相关问题