&[T]
令我困惑 .
我天真地认为,像 &T
, &[T]
是一个指针,也就是说,一个数字指针地址 .
但是,我已经看到了一些像这样的代码,我看到工作正常(为了演示目的而简化;但是你在许多'as_slice()'实现中看到这样的代码)我感到非常惊讶:
extern crate core;
extern crate collections;
use self::collections::str::raw::from_utf8;
use self::core::raw::Slice;
use std::mem::transmute;
fn main() {
let val = "Hello World";
{
let value:&str;
{
let bytes = val.as_bytes();
let mut slice = Slice { data: &bytes[0] as *const u8, len: bytes.len() };
unsafe {
let array:&[u8] = transmute(slice);
value = from_utf8(array);
}
// slice.len = 0;
}
println!("{}", value);
}
}
所以 .
我最初认为这是无效的代码 .
也就是说,在块作用域内创建的 Slice
实例将返回到块作用域之外(通过转换),尽管代码运行, println!
实际上是通过不安全指针访问不再有效的数据 . 坏!
......但似乎并非如此 .
考虑评论一下 // slice.len = 0;
发生这种情况时,此代码仍可正常运行(打印“Hello World”) .
这条线......
value = from_utf8(array);
如果它是指向'slice'变量的无效指针, println()
语句中的 len
将为0,但事实并非如此 . 因此,不仅是指针值的副本,而且是 Slice
结构的完整副本 .
是对的吗?
这是否意味着一般来说只要实际的内部数据指针有效,它就会返回 &[T]
,无论正在返回的原始 &[T]
的范围是什么,因为 &[T]
赋值是一个复制操作?
(对我来说,这似乎是非常反直觉......所以也许我是误解;如果我是对的,有两个 &[T]
指向相同的数据是无效的,因为如果你修改它们将不会同步长度一...)
1 回答
正如您所注意到的那样,切片
&[T]
对于结构std::raw::Slice
是"equivalent" . 实际上,Slice
是&[T]
值的内部表示,是的,它是指针和该指针后面的数据长度 . 有时这种结构称为"fat pointer",即指针和附加信息 .当您传递
&[T]
值时,您确实只是复制其内容 - 指针和长度 .所以,是的,确切地说 .
这也是事实 . 这是借来的参考文献的整个想法,包括切片 - 借用的参考文献被静态检查,只要它们的指示物存活就可以使用 . 当DST最终落地时,切片和常规参考将更加统一 .
这实际上是一个绝对有效的问题;这是别名的问题之一 . 但是,Rust的设计正是为了防止此类错误 . 有两种方法可以使切片的别名有效 .
首先,切片不能改变长度;没有在
&[T]
上定义的方法可以让你改变它的长度 . 您可以从切片创建派生切片,但它将是一个新对象 .但即使切片不能改变长度,如果数据可以通过它们进行变异,如果别名也会带来灾难 . 例如,如果slice中的值是枚举实例,则改变此类别名切片中的值可能会使指向此切片中包含的枚举值的内部的指针无效 . 所以,第二,Rust别名切片(
&[T]
)是不可变的 . 您可以___859562_ t将可变引用纳入其中 .这两个功能(以及编译器检查生命周期)使切片的别名绝对安全 . 但是,有时您需要修改切片中的数据 . 然后你需要可变片,称为
&mut [T]
. 您可以通过这样的切片更改数据;但这些切片不是别的 . 你可以做任何危险的事情 .但请注意,使用
transmute()
将切片转换为Slice
(反之亦然)是一种不安全的操作 . 如果使用正确的方法创建&[T]
,则保证静态正确,例如在Vec
上调用as_slice()
. 但是,使用Slice
struct手动创建它然后将其转换为&[T]
是容易出错的,并且可以轻松地对程序进行分段,例如,当您为其分配的长度超过实际分配的长度时 .