8

我知道 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 尝试(在 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);
}

但它被拒绝了:

[&mut [f32]]不能从类型元素的迭代器构建类型集合&mut [f32]

可悲的是,这听起来和我想要做的完全一样!

我尝试过使用固定大小的数组,但 Rust 不支持它们的泛型,所以我不能从迭代器中填充它,也不能在类似 C 的循环中填充它们,因为引用被引入循环不会超过它。

该特征core::iter::FromIterator<&mut [f32]>未针对该类型实现[&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);
}

错误:一次不能多次借用img.chanmutable

4

1 回答 1

6

arrayvec是一个库,可以满足您的需求。(另外,您可能想要iter_mutandas_mut而不是iterand 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使用的。)

游戏围栏

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].

于 2015-05-24T22:31:47.687 回答