自动将Vec转换为数组

7

在尝试不同的方法将Vec<u8>发送到预期为&[u8]的函数时,我犯了一个“错误”,但实际上它起作用了。以下是我编写的一些示例代码:

fn sum(v: &[u8]) -> u8 {
    let mut s = 0u8;
    for x in v.iter() {
        s = s + x;
    }
    return s;
}

fn main() {
    let myarr = [1u8, 2u8, 3u8];
    let myvec = vec![1u8, 2u8, 3u8];

    println!("{}", sum(&myarr));
    println!("{}", sum(&myvec));
}

关于这个问题,我有几个疑问:

  • 为什么以及如何起作用?
  • 这两种类型之间是否存在自动转换?
  • 它会产生任何惩罚还是只是获取向量底层数组的内存位置?
  • 这是否意味着对于这种类型的使用(在数字数组上进行只读操作),最好使用数组而不是向量作为API?
2个回答

10

让我们先澄清一件事情:在您的sum函数中没有数组。 Rust有三种相关类型,您可以在Stack Overflow或者Rust编程语言中搜索更多信息:

  1. 切片&[T]
  2. 向量Vec<T>
  3. 数组[T; n]

v参数是一个切片,它只是指向数据块和元素数量的指针。

为什么这样会起作用?这两种类型之间是否存在自动转换?

Deref trait在这里发挥了作用。有一个实现该trait的方法如下:

impl<T> Deref for Vec<T> {
    type Target = [T];
    fn deref(&self) -> &[T];
}

这意味着任何对 Vec<T> 的引用都可以作为对 [T] 的引用,并获得目标类型的所有方法。编译器理解 Deref,但是任何类型都能够实现它,所以它只是有点特殊。
“零成本转换”意味着没有额外开销,这就是为什么编译器会自动处理它的原因。很少有编译器会为你做昂贵的事情。
绝对是的!100%的时间里,你应该接受一个 &[T] 而不是一个 &Vec<T>。提供 &[T] 的东西不止 Vec,数组就是其中的一个例子。

@BenjaminLindley 很好的发现; 看看我这张脸上的蛋! 我只看了 sum 函数... 我已经更新了答案,使其更清晰。 - Shepmaster
好的,说实话我不确定你是犯了错误还是我的 Rust 类型理解有误。 - Benjamin Lindley
@BenjaminLindley 没门!相信自己!我和 Stack Overflow 上的大多数人一样,拥有一个普通的、可能会出错的人脑 ^_^ - Shepmaster
有关函数的返回值,您有什么好的建议吗?它们应该返回 [u8] 还是 Vec<u8>,或者应该将 Vec<u8> 作为参数传递? - Hernan
@Hernan,你实际上不能返回一个 [u8]。你能否返回一个 &[u8] 取决于你的情况,但通常更节省内存。其他情况使用 Vec<u8> - Shepmaster

3
您正在观看Deref特质的使用。如果您有一个实现了此特质的类型T,并且您调用一个需要参数为&U类型的函数,则&T将调用deref函数以使值变为正确的类型。本书有一章节介绍此内容。
您可以在此链接中查看Vec的实现方式。它只是将Vec的指针和长度字段复制到一个新结构体中,这几乎是免费的。
另外,确实更好地编写接受视图而不是拥有类型的函数。只能使用Vec<u8>调用的函数只能被Vec<u8>调用,但是使用&[u8]作为参数的函数可以由其他位置的Vec<u8>[u8; 500]&[u8]调用。同样,函数应尽可能采用&T而不是Box<T>,或采用&str而不是String

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