英文:
How to get a working mutable reference to a subset of an array?
问题
以下是代码部分的翻译:
This works as expected for reading:
fn u64_from_low_eight(buf: &[u8; 9]) -> u64 {
let bytes: &[u8; size_of::<u64>()] = buf[..size_of::<u64>()].try_into().unwrap();
u64::from_le_bytes(*bytes)
}
(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.
/// Encodes a u64 into 1-9 bytes and returns the number of bytes updated.
pub fn encode(value: u64, buf: &mut [u8; 9]) -> usize {
let low64: &mut [u8; size_of::<u64>()] = &mut buf[..(size_of::<u64>())].try_into().unwrap();
match value {
// FIXME: Change to exclusive ranges once the feature's stabilised.
OFFSET0..=OFFSET1_LESS_ONE => {
let num = inner_encode::<1>(value, low64);
#[cfg(test)] eprintln!("low64: {low64:?}");
#[cfg(test)] eprintln!("buf: {buf:?}");
num
},
low64
(上面的代码)似乎不是对buf
的前八个字节的可变引用。 (也许它指向了一个副本?)
也就是说,在上面的示例中,low64
和 buf
的前八个字节是不同的。
我可以使用以下代码替代 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
use std::mem::size_of;
fn u64_to_low_eight(value: u64, buf: &mut [u8; 9]) {
let low64: &mut [u8; size_of::<u64>()] = &mut buf[..size_of::<u64>()].try_into().unwrap();
*low64 = u64::to_le_bytes(value);
dbg!(low64);
}
fn main() {
let mut src: [u8; 9] = [1, 2, 3, 4, 5, 6, 7, 8, 9];
u64_to_low_eight(0x0A_0B_0C_0D_0E_0F_10_11, &mut src);
dbg!(src);
}
以及输出,我希望 src
也发生变化:
Compiling playground v0.0.1 (/playground)
Finished dev [unoptimized + debuginfo] target(s) in 0.62s
Running `target/debug/playground`
[src/main.rs:6] low64 = [
17,
16,
15,
14,
13,
12,
11,
10,
]
[src/main.rs:12] src = [
1,
2,
3,
4,
5,
6,
7,
8,
9,
]
希望这些信息有所帮助。
英文:
This works as expected for reading:
fn u64_from_low_eight(buf: &[u8; 9]) -> u64 {
let bytes: &[u8; size_of::<u64>()] = buf[..size_of::<u64>()].try_into().unwrap();
u64::from_le_bytes(*bytes)
}
(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.
/// Encodes a u64 into 1-9 bytes and returns the number of bytes updated.
pub fn encode(value: u64, buf: &mut [u8; 9]) -> usize {
let low64: &mut [u8; size_of::<u64>()] = &mut buf[..(size_of::<u64>())].try_into().unwrap();
match value {
// FIXME: Change to exclusive ranges once the feature's stabilised.
OFFSET0..=OFFSET1_LESS_ONE => {
let num = inner_encode::<1>(value, low64);
#[cfg(test)] eprintln!("low64: {low64:?}");
#[cfg(test)] eprintln!("buf: {buf:?}");
num
},
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: &mut [u8; size_of::<u64>()] = &mut buf[..(size_of::<u64>())].try_into().unwrap();
to get a &mut [u8; 8]
which points at the first eight bytes of a &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&mode=debug&edition=2021&gist=294a9dd9ba1400eceb2d945a10028a6b
use std::mem::size_of;
fn u64_to_low_eight(value: u64, buf: &mut [u8; 9]) {
let low64: &mut [u8; size_of::<u64>()] = &mut buf[..size_of::<u64>()].try_into().unwrap();
*low64 = u64::to_le_bytes(value);
dbg!(low64);
}
fn main() {
let mut src: [u8; 9] = [1, 2, 3, 4, 5, 6, 7, 8, 9];
u64_to_low_eight(0x0A_0B_0C_0D_0E_0F_10_11, &mut src);
dbg!(src);
}
and the output, where I want src
to change too:
Compiling playground v0.0.1 (/playground)
Finished dev [unoptimized + debuginfo] target(s) in 0.62s
Running `target/debug/playground`
[src/main.rs:6] low64 = [
17,
16,
15,
14,
13,
12,
11,
10,
]
[src/main.rs:12] src = [
1,
2,
3,
4,
5,
6,
7,
8,
9,
]
答案1
得分: 5
需要将&mut buf[..size_of::<u64>()]
包裹在括号中。&mut
是一个低优先级操作符,因此右侧的所有内容先进行评估,使您的代码等同于:
// 获取对这8个复制字节的可变引用
let low64: &mut [u8; size_of::<u64>()] = &mut (
// 从`buf`中复制8个字节
buf[..size_of::<u64>()].try_into().unwrap()
);
所以您需要改为以下方式:
let low64: &mut [u8; size_of::<u64>()] = (&mut buf[..size_of::<u64()>]).try_into().unwrap();
英文:
You need to wrap &mut buf[..size_of::<u64>()]
in parenthesis. &mut
is a low-priority operator, so everything to the right is evaluated first, making your code equivalent to:
// takes a mutable reference to those 8 copied bytes
let low64: &mut [u8; size_of::<u64>()] = &mut (
// copies 8 bytes out of `buf`
buf[..size_of::<u64>()].try_into().unwrap()
);
So you need to do the following instead:
let low64: &mut [u8; size_of::<u64>()] = (&mut buf[..size_of::<u64>()]).try_into().unwrap();
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论