如何将2D向量作为片段传递给函数?

5

对于一维,代码模拟如下:

fn main() {
    let a = vec![1, 2, 3, 4, 5];
    print_it(&a); // :)
}

fn print_it(p: &[usize]) {
    println!("{:?}", p);
}

我试图将等效的逻辑应用于二维向量,但它不起作用。

fn main() {
    let a: Vec<Vec<usize>> = vec![
        vec![1,2,3,4,],
        vec![5,6,7,8,],
    ];
    print_it(&a); // :( expected slice `[&[usize]]`, found struct `Vec`
}

fn print_it(p: &[&[usize]] ) {
    println!("{:?}", p);
}

我做错了什么?


请阅读完整的错误信息:note: expected reference '&[&[usize]]' found reference '&Vec<Vec<usize>>' 该函数期望一个 usize 的切片,但您传递了一个 VecVec 的引用。 - Herohtar
这条消息说类型是错误的,当然。这就是我不理解的地方。哪种类型才是回答我的问题的正确类型? - Alexandra Danith Ansley
2
为了使它工作,您需要一个Vec <&[usize]>,或者将print_it更改为接受&[Vec<usize>](甚至是&Vec<Vec<usize>>)。 - Herohtar
&[Vec<usize>] 是可行的。但是,我能否将内部向量转换为其他切片,即切片的切片? - Alexandra Danith Ansley
2个回答

5

如果没有额外的工作,这种转换是不可能的。从slice类型的官方文档可以看到:

动态大小的视图,[T],可以连续查看序列。 连续在这里意味着元素按照每个元素到其相邻元素的距离排列。

这意味着指向的内存看起来像这样:

| T | T | ... | T |

切片是一个视图,它表示为指针和长度的内存块。

这意味着类型[&[usize]]在内存中可能是这样的:

| pointer, length | pointer, length | ... | pointer, length |

然而,你有一个向量的向量。一个向量有指向其分配内存的指针和两个usize,一个用于长度,一个用于容量。因此,[Vec<T>]

| pointer, length, capacity | pointer, length, capacity | ... | pointer, length, capacity |

Vec<Vec<T>> 在其内部缓冲区中也具有这种布局。由于指针-长度对的后一种布局不是连续的(容量 usize 的空间在中间),我们不能将指向该内存的指针重新解释为 &[&[T]]


如果您确实需要2D切片,您必须分配一个具有指针-长度布局的新缓冲区:

let v: Vec<Vec<usize>> = todo!();
let slice2d: Vec<&[usize]> = v.iter().map(Vec::as_slice).collect();

游乐场

如果你不想将函数签名限制为Vec,你可以使它更加通用:

use std::ops::Index;

fn print_it<I>(p: &[I])
where
    I: Index<usize, Output = usize>,
{
    println!("{:?}", p[0][0]);
}

游乐场


4
问题不在于函数签名。你需要一个Vec<&[usize]>,这样最终可以得到一个&[&[usize]]。例如,把切片收集到另一个向量中:
let a_refs: Vec<&[usize]> = a.iter().map(|v| v.as_slice()).collect();

完整示例:

fn main() {
    let a: Vec<Vec<usize>> = vec![
        vec![1,2,3,4,],
        vec![5,6,7,8,],
    ];
    let a_refs: Vec<&[usize]> = a.iter().map(|v| v.as_slice()).collect();
    print_it(&a_refs); // :( expected slice `[&[usize]]`, found struct `Vec`
}

fn print_it(p: &[&[usize]] ) {
    println!("{:?}", p);
}

Playground

注意新向量仅包含对原向量切片的引用。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接