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

huangapple go评论60阅读模式
英文:

How to "take" the first part of a slice borrowed in a function?

问题

背景

我对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],  // 这应该是一个包含10字节的切片。
}

impl<'a> MyStruct<'a> {
    fn from_bytes(mut data: &'a mut [u8]) -> Result<MyStruct<'a>, &'static str> {
        if data.len() < 10 {
            return Err("需要10字节。");
        }
        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]: 无法同时多次可变借用`bytes`
  --> src\main.rs:10:39
   |
9  |     let mut s1 = MyStruct::from_bytes(&mut bytes).unwrap();
   |                                       ---------- 首次可变借用发生在此处
10 |     let mut s2 = MyStruct::from_bytes(&mut bytes).unwrap();
   |                                       ^^^^^^^^^^ 第二次可变借用发生在此处
11 |     println!("{:?}", s1);
   |                      -- 首次借用稍后在此处使用

请问您有什么具体的问题或需求需要解决?

英文:

Background

I'm fairly new to Rust, and I'm trying to write a packet parser. It's in a #[no_std] environment with no heap.

The input to the parser is a mutable reference to a slice of bytes (&mut [u8]), and the output is a heapless::Vec of packet header structs (really each is just a struct with a reference to its slice and some functions to obtain the header fields).

Problem

I'm struggling to split the initial provided slice into the smaller slices required for the individual packet headers. Borrowing the slice in a function and using slice.take_mut() to get the first piece seems to not return permissions for the rest of the slice, so a second call to the function doesn't compile.

I've also tried an implementation using slice.split_at_mut() with similar problems.

It doesn't help that I'm struggling to understand lifetimes and how they fit into all of this.

Example

I've tried to reduce my code to a minimal example, but I might've missed out on some important context in doing so.

#![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})
    }
}

What I was expecting:

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]

What actually happens:

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

答案1

得分: 4

以下是您要翻译的代码的部分:

第一处问题是,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],  // 这应该是一个包含 10 字节的切片。
}

impl<'a> MyStruct<'a> {
    fn from_bytes(data: &mut &'a mut [u8]) -> Result<MyStruct<'a>, &'static str> {
        if data.len() < 10 {
            return Err("需要 10 个字节。");
        }
        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],  // 这应该是一个包含 10 字节的切片。
}

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

希望这对您有所帮助。如果您有其他问题,请随时提问。

英文:

You are close here, but you've got two problems.

The first is that from_bytes needs to take a mutable reference to a mutable slice. This allows it to set the caller-provided slice to a subregion.

The second is that you are passing in &amp;mut bytes instead of a slice -- you need to pass in a mutable reference to a slice so that from_bytes can adjust that slice to point to a subregion.

Fixing both of these issues:

#![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 = &amp;mut bytes[..];
    let s1 = MyStruct::from_bytes(&amp;mut b).unwrap();
    let s2 = MyStruct::from_bytes(&amp;mut b).unwrap();
    println!(&quot;{:?}&quot;, s1);
    println!(&quot;{:?}&quot;, s2);
    println!(&quot;{:?}&quot;, b);
}

#[derive(Debug)]
struct MyStruct&lt;&#39;a&gt; {
    data: &amp;&#39;a mut [u8],  // This should be a 10-byte slice.
}

impl&lt;&#39;a&gt; MyStruct&lt;&#39;a&gt; {
    fn from_bytes(data: &amp;mut &amp;&#39;a mut [u8]) -&gt; Result&lt;MyStruct&lt;&#39;a&gt;, &amp;&#39;static str&gt; {
        if data.len() &lt; 10 {
            return Err(&quot;Need 10 bytes.&quot;);
        }
        let struct_data = data.take_mut(..10).unwrap();
        Ok(MyStruct{data: struct_data})
    }
}

(Playground)

Note that you can make this work on stable Rust by using split_at_mut instead. This requires a bit of gymnastics involving std::mem::take() -- in fact, this is exactly how take_mut is implemented under the hood!

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 = &amp;mut bytes[..];
    let s1 = MyStruct::from_bytes(&amp;mut b).unwrap();
    let s2 = MyStruct::from_bytes(&amp;mut b).unwrap();
    println!(&quot;{:?}&quot;, s1);
    println!(&quot;{:?}&quot;, s2);
    println!(&quot;{:?}&quot;, b);
}

#[derive(Debug)]
struct MyStruct&lt;&#39;a&gt; {
    data: &amp;&#39;a mut [u8],  // This should be a 10-byte slice.
}

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

(Playground)

答案2

得分: 0

I don't fully understand the implications of being in a no_std environment, but maybe it would be useful to encapsulate the disunion logic in another structure.

#[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 = Some(value);
        }

        parts
    }
}

playground

英文:

I don't fully understand the implications of being in a no_std environment, but maybe it would be useful to encapsulate the disunion logic in another structure.

#[derive(Debug)]
struct Parts&lt;&#39;a&gt; {
    parts: Vec&lt;MyStruct&lt;&#39;a&gt;&gt;,
    remain: Option&lt;&amp;&#39;a mut [u8]&gt;,
}

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

        parts
    }
}

playground

huangapple
  • 本文由 发表于 2023年4月13日 16:22:00
  • 转载请务必保留本文链接:https://go.coder-hub.com/76003226.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定