如何获得数组子集的可变引用?

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

How to get a working mutable reference to a subset of an array?

问题

以下是代码部分的翻译:

  1. This works as expected for reading:
  2. fn u64_from_low_eight(buf: &[u8; 9]) -> u64 {
  3. let bytes: &[u8; size_of::<u64>()] = buf[..size_of::<u64>()].try_into().unwrap();
  4. u64::from_le_bytes(*bytes)
  5. }
  6. (It nicely optimises into a single assembly instruction on AArch64 and x86_64.)
  7. I had expected something similar to work for an unaligned write of a u64 to a buffer.
  8. /// Encodes a u64 into 1-9 bytes and returns the number of bytes updated.
  9. pub fn encode(value: u64, buf: &mut [u8; 9]) -> usize {
  10. let low64: &mut [u8; size_of::<u64>()] = &mut buf[..(size_of::<u64>())].try_into().unwrap();
  11. match value {
  12. // FIXME: Change to exclusive ranges once the feature's stabilised.
  13. OFFSET0..=OFFSET1_LESS_ONE => {
  14. let num = inner_encode::<1>(value, low64);
  15. #[cfg(test)] eprintln!("low64: {low64:?}");
  16. #[cfg(test)] eprintln!("buf: {buf:?}");
  17. num
  18. },

low64(上面的代码)似乎不是对buf的前八个字节的可变引用。 (也许它指向了一个副本?)

也就是说,在上面的示例中,low64buf 的前八个字节是不同的。

我可以使用以下代码替代 let low64: &mut [u8; size_of::<u64>()] = &mut buf[..(size_of::<u64>())].try_into().unwrap(); 来获取一个指向 &mut [u8; 9] 的前八个字节的 &mut [u8; 8] 吗?

(我的意图是这也应该在 AArch64 和 x86_64 上优化为单个非对齐写入。)

更新:这是在 Playground 上的问题链接:https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=294a9dd9ba1400eceb2d945a10028a6b

  1. use std::mem::size_of;
  2. fn u64_to_low_eight(value: u64, buf: &mut [u8; 9]) {
  3. let low64: &mut [u8; size_of::<u64>()] = &mut buf[..size_of::<u64>()].try_into().unwrap();
  4. *low64 = u64::to_le_bytes(value);
  5. dbg!(low64);
  6. }
  7. fn main() {
  8. let mut src: [u8; 9] = [1, 2, 3, 4, 5, 6, 7, 8, 9];
  9. u64_to_low_eight(0x0A_0B_0C_0D_0E_0F_10_11, &mut src);
  10. dbg!(src);
  11. }

以及输出,我希望 src 也发生变化:

  1. Compiling playground v0.0.1 (/playground)
  2. Finished dev [unoptimized + debuginfo] target(s) in 0.62s
  3. Running `target/debug/playground`
  4. [src/main.rs:6] low64 = [
  5. 17,
  6. 16,
  7. 15,
  8. 14,
  9. 13,
  10. 12,
  11. 11,
  12. 10,
  13. ]
  14. [src/main.rs:12] src = [
  15. 1,
  16. 2,
  17. 3,
  18. 4,
  19. 5,
  20. 6,
  21. 7,
  22. 8,
  23. 9,
  24. ]

希望这些信息有所帮助。

英文:

This works as expected for reading:

  1. fn u64_from_low_eight(buf: &amp;[u8; 9]) -&gt; u64 {
  2. let bytes: &amp;[u8; size_of::&lt;u64&gt;()] = buf[..size_of::&lt;u64&gt;()].try_into().unwrap();
  3. u64::from_le_bytes(*bytes)
  4. }

(It nicely optimises into a single assembly instruction on AArch64 and x86_64.)

I had expected something similar to work for an unaligned write of a u64 to a buffer.

  1. /// Encodes a u64 into 1-9 bytes and returns the number of bytes updated.
  2. pub fn encode(value: u64, buf: &amp;mut [u8; 9]) -&gt; usize {
  3. let low64: &amp;mut [u8; size_of::&lt;u64&gt;()] = &amp;mut buf[..(size_of::&lt;u64&gt;())].try_into().unwrap();
  4. match value {
  5. // FIXME: Change to exclusive ranges once the feature&#39;s stabilised.
  6. OFFSET0..=OFFSET1_LESS_ONE =&gt; {
  7. let num = inner_encode::&lt;1&gt;(value, low64);
  8. #[cfg(test)] eprintln!(&quot;low64: {low64:?}&quot;);
  9. #[cfg(test)] eprintln!(&quot;buf: {buf:?}&quot;);
  10. num
  11. },

low64 (above) appears not to be a mutable reference into the first eight bytes of buf. (Perhaps it is pointing at a copy?)

i.e. low64 and the first eight bytes of buf are different in the example above.

What can I use instead of let low64: &amp;mut [u8; size_of::&lt;u64&gt;()] = &amp;mut buf[..(size_of::&lt;u64&gt;())].try_into().unwrap(); to get a &amp;mut [u8; 8] which points at the first eight bytes of a &amp;mut [u8; 9]?

(My intention is that this should also optimise into a single unaligned write on AArch64 and x86_64.)


Update: Here's the problem on the playground: https://play.rust-lang.org/?version=stable&amp;mode=debug&amp;edition=2021&amp;gist=294a9dd9ba1400eceb2d945a10028a6b

  1. use std::mem::size_of;
  2. fn u64_to_low_eight(value: u64, buf: &amp;mut [u8; 9]) {
  3. let low64: &amp;mut [u8; size_of::&lt;u64&gt;()] = &amp;mut buf[..size_of::&lt;u64&gt;()].try_into().unwrap();
  4. *low64 = u64::to_le_bytes(value);
  5. dbg!(low64);
  6. }
  7. fn main() {
  8. let mut src: [u8; 9] = [1, 2, 3, 4, 5, 6, 7, 8, 9];
  9. u64_to_low_eight(0x0A_0B_0C_0D_0E_0F_10_11, &amp;mut src);
  10. dbg!(src);
  11. }

and the output, where I want src to change too:

  1. Compiling playground v0.0.1 (/playground)
  2. Finished dev [unoptimized + debuginfo] target(s) in 0.62s
  3. Running `target/debug/playground`
  4. [src/main.rs:6] low64 = [
  5. 17,
  6. 16,
  7. 15,
  8. 14,
  9. 13,
  10. 12,
  11. 11,
  12. 10,
  13. ]
  14. [src/main.rs:12] src = [
  15. 1,
  16. 2,
  17. 3,
  18. 4,
  19. 5,
  20. 6,
  21. 7,
  22. 8,
  23. 9,
  24. ]

答案1

得分: 5

需要将&amp;mut buf[..size_of::<u64>()] 包裹在括号中。&amp;mut 是一个低优先级操作符,因此右侧的所有内容先进行评估,使您的代码等同于:

  1. // 获取对这8个复制字节的可变引用
  2. let low64: &amp;mut [u8; size_of::<u64>()] = &amp;mut (
  3. // 从`buf`中复制8个字节
  4. buf[..size_of::<u64>()].try_into().unwrap()
  5. );

所以您需要改为以下方式:

  1. let low64: &amp;mut [u8; size_of::<u64>()] = (&amp;mut buf[..size_of::<u64()>]).try_into().unwrap();
英文:

You need to wrap &amp;mut buf[..size_of::&lt;u64&gt;()] in parenthesis. &amp;mut is a low-priority operator, so everything to the right is evaluated first, making your code equivalent to:

  1. // takes a mutable reference to those 8 copied bytes
  2. let low64: &amp;mut [u8; size_of::&lt;u64&gt;()] = &amp;mut (
  3. // copies 8 bytes out of `buf`
  4. buf[..size_of::&lt;u64&gt;()].try_into().unwrap()
  5. );

So you need to do the following instead:

  1. let low64: &amp;mut [u8; size_of::&lt;u64&gt;()] = (&amp;mut buf[..size_of::&lt;u64&gt;()]).try_into().unwrap();

huangapple
  • 本文由 发表于 2023年2月19日 05:56:20
  • 转载请务必保留本文链接:https://go.coder-hub.com/75496635.html
匿名

发表评论

匿名网友

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

确定