如何将 C 可变长度数组代码转换为 Rust?
How to convert C variable-length array code to Rust?
我知道 Rust 不支持可变长度数组,但这让我想知道用什么来替换它们,因为:
- 我不想在循环中分配和释放一个小的 Vec
- 借用检查器不允许我将代码移出循环
- 固定大小的数组有很多限制,所以我不知道如何使用它们
我正在转换的 C 代码通过在每一行调用回调并传递一个小指针数组来处理图像:
float *tmp[img->channels]; // Small, up to 4 elements
for(int y = 0; y < height; y++) {
for(int ch = 0; ch < img->channels; ch++) {
tmp[ch] = &img->channel[ch]->pixels[width * y];
}
callback(tmp, img->channels);
}
我的 Rust 尝试 (example in playpen):
for y in 0..height {
let tmp = &img.channel.iter().map(|channel| {
&mut channel.pixels.as_ref().unwrap()[width * y .. width * (y+1)]
}).collect();
callback(tmp);
}
但是被拒绝了:
a collection of type [&mut [f32]]
cannot be built from an iterator over elements of type &mut [f32]
遗憾的是,这听起来和我想做的一模一样!
我试过使用固定大小的数组,但 Rust 不支持它们的泛型,所以我无法从迭代器填充它,也无法在类似 C 的循环中填充它们,因为循环中的引用不会比它长。
the trait core::iter::FromIterator<&mut [f32]>
is not implemented for the type [&mut [f32]; 4]
另一种从固定大小数组中获取内存片段的方法也失败了:
let mut row_tmp: [&mut [f32]; 4] = unsafe{mem::zeroed()};
for y in 0..height {
row_tmp[0..channels].iter_mut().zip(img.chan.iter_mut()).map(|(t, chan)| {
*t = &mut chan.img.as_ref().unwrap()[(width * y) as usize .. (width * (y+1)) as usize]
});
cb(&row_tmp[0..channels], y, width, image_data);
}
error: cannot borrow img.chan
as mutable more than once at a time
arrayvec
是一个可以满足您需求的库。 (此外,您可能需要 iter_mut
和 as_mut
而不是 iter
和 as_ref
。)
for y in 0..height {
let tmp: ArrayVec<[_; 4]> = img.channel.iter_mut().map(|channel| {
&mut channel.pixels.as_mut().unwrap()[width * y .. width * (y+1)]
}).collect();
callback(&tmp);
}
它在堆栈上分配固定数量的存储空间(此处为 4 个项目),其行为类似于 Vec
,其大小是有限的(最大为编译时指定的容量)但可变。
arrayvec
中的大部分复杂性是处理 运行 可变数量项目的析构函数。但是由于 &mut _
没有析构函数,你也可以只使用固定大小的数组。但是你必须使用 unsafe
代码并且注意不要读取未初始化的项目。 (固定大小的数组不实现 FromIterator
,这是 Iterator::collect
使用的。)
(Playpen)
let n_channels = img.channel.len();
for y in 0..height {
let tmp: [_; 4] = unsafe { mem::uninitialized() }
for (i, channel) in img.channel.iter_mut().enumerate() {
tmp[i] = &mut channel.pixels.as_mut().unwrap()[width * y .. width * (y+1)];
}
// Careful to only touch initialized items...
callback(&tmp[..n_channels]);
}
编辑:不安全代码可以替换为:
let mut tmp: [&mut [_]; 4] = [&mut [], &mut [], &mut [], &mut []];
较短的 [&mut []; 4]
初始化语法不适用于此处,因为 &mut [_]
不可隐式复制。类型注释是必要的,这样你就不会得到 [&mut [_; 0]; 4]
.
我知道 Rust 不支持可变长度数组,但这让我想知道用什么来替换它们,因为:
- 我不想在循环中分配和释放一个小的 Vec
- 借用检查器不允许我将代码移出循环
- 固定大小的数组有很多限制,所以我不知道如何使用它们
我正在转换的 C 代码通过在每一行调用回调并传递一个小指针数组来处理图像:
float *tmp[img->channels]; // Small, up to 4 elements
for(int y = 0; y < height; y++) {
for(int ch = 0; ch < img->channels; ch++) {
tmp[ch] = &img->channel[ch]->pixels[width * y];
}
callback(tmp, img->channels);
}
我的 Rust 尝试 (example in playpen):
for y in 0..height {
let tmp = &img.channel.iter().map(|channel| {
&mut channel.pixels.as_ref().unwrap()[width * y .. width * (y+1)]
}).collect();
callback(tmp);
}
但是被拒绝了:
a collection of type
[&mut [f32]]
cannot be built from an iterator over elements of type&mut [f32]
遗憾的是,这听起来和我想做的一模一样!
我试过使用固定大小的数组,但 Rust 不支持它们的泛型,所以我无法从迭代器填充它,也无法在类似 C 的循环中填充它们,因为循环中的引用不会比它长。
the trait
core::iter::FromIterator<&mut [f32]>
is not implemented for the type[&mut [f32]; 4]
另一种从固定大小数组中获取内存片段的方法也失败了:
let mut row_tmp: [&mut [f32]; 4] = unsafe{mem::zeroed()};
for y in 0..height {
row_tmp[0..channels].iter_mut().zip(img.chan.iter_mut()).map(|(t, chan)| {
*t = &mut chan.img.as_ref().unwrap()[(width * y) as usize .. (width * (y+1)) as usize]
});
cb(&row_tmp[0..channels], y, width, image_data);
}
error: cannot borrow
img.chan
as mutable more than once at a time
arrayvec
是一个可以满足您需求的库。 (此外,您可能需要 iter_mut
和 as_mut
而不是 iter
和 as_ref
。)
for y in 0..height {
let tmp: ArrayVec<[_; 4]> = img.channel.iter_mut().map(|channel| {
&mut channel.pixels.as_mut().unwrap()[width * y .. width * (y+1)]
}).collect();
callback(&tmp);
}
它在堆栈上分配固定数量的存储空间(此处为 4 个项目),其行为类似于 Vec
,其大小是有限的(最大为编译时指定的容量)但可变。
arrayvec
中的大部分复杂性是处理 运行 可变数量项目的析构函数。但是由于 &mut _
没有析构函数,你也可以只使用固定大小的数组。但是你必须使用 unsafe
代码并且注意不要读取未初始化的项目。 (固定大小的数组不实现 FromIterator
,这是 Iterator::collect
使用的。)
(Playpen)
let n_channels = img.channel.len();
for y in 0..height {
let tmp: [_; 4] = unsafe { mem::uninitialized() }
for (i, channel) in img.channel.iter_mut().enumerate() {
tmp[i] = &mut channel.pixels.as_mut().unwrap()[width * y .. width * (y+1)];
}
// Careful to only touch initialized items...
callback(&tmp[..n_channels]);
}
编辑:不安全代码可以替换为:
let mut tmp: [&mut [_]; 4] = [&mut [], &mut [], &mut [], &mut []];
较短的 [&mut []; 4]
初始化语法不适用于此处,因为 &mut [_]
不可隐式复制。类型注释是必要的,这样你就不会得到 [&mut [_; 0]; 4]
.