& [T]字面上是Slice中的一个别名吗?

时间:2014-07-30 02:15:29

标签: rust

&[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'变量的无效指针,len语句中的println()将为0,但事实并非如此。这样不仅可以复制指针值,还可以复制Slice结构的完整副本。

是吗?

这是否意味着通常只要实际的内部数据指针有效,它就会返回&[T],无论返回的原始&[T]的范围如何,因为{ {1}}赋值是复制操作吗?

(对我来说,这似乎是非常反直觉的......所以也许我是误会;如果我是对的,有两个&[T]指向相同的数据是无效的,因为他们赢了如果修改一个,则不要同步长度......)

1 个答案:

答案 0 :(得分:14)

正如您所注意到的,切片&[T]与结构std::raw::Slice“等效”。实际上,Slice&[T]值的内部表示,是的,它是指针和指针后面的数据长度。有时这种结构称为“胖指针”,即指针和附加信息。

当您传递&[T]值时,您确实只是复制其内容 - 指针和长度。

  

如果它是指向'slice'变量的无效指针,则pr​​intln()语句中的len将为0,但实际上不是。因此,不仅是指针值的副本,而且是Slice结构的完整副本。   是吗?

所以,是的,确实。

  

这是否意味着一般来说只要实际内部数据指针有效就返回& [T]有效,无论返回的原始& [T]的范围如何,因为& ; [T]赋值是复制操作吗?

这也是事实。这是借来的参考文献的整个想法,包括切片 - 借用的参考文献被静态检查,只要它们的指示物存活就可以使用。当DST最终落地时,切片和常规参考将更加统一。

  

(对我来说,这似乎是非常反直觉......所以也许我是误解;如果我是对的,有两个& [T]指向相同的数据是无效的,因为他们赢了如果修改一个,则不要同步长度......)

这实际上是一个绝对有效的问题;这是别名的问题之一。但是,Rust的设计正是为了防止此类错误。有两种方法可以使切片的别名有效。

首先,切片不能改变长度; &[T]上没有定义任何方法可以让您更改其长度。您可以从切片创建派生切片,但它将是一个新对象。

但即使切片无法改变长度,如果数据可以通过它们进行变异,如果别名也会带来灾难。例如,如果slice中的值是枚举实例,则改变此类别名切片中的值可能会使指向此切片中包含的枚举值的内部的指针无效。所以,第二,Rust可别的切片(&[T])是不可变的。您无法更改其中包含的值,也无法将可变引用添加到其中。

这两个功能(以及编译器检查生命周期)使切片的别名绝对安全。但是,有时您需要修改切片中的数据。然后你需要 mutable 切片,称为&mut [T]。您可以通过这样的切片更改数据;但这些切片不可混淆。你不能在同一个结构(例如一个数组)中创建两个可变切片,所以你不能做任何危险的事情。

但请注意,使用transmute()将切片转换为Slice或反之亦然是一种不安全的操作。如果您使用正确的方法创建&[T],则as_slice()保持静态正确,例如在Vec上调用Slice。但是,使用&[T] struct手动创建它然后将其转换为{{1}}是容易出错的,并且很容易将程序分段,例如,当您为其分配的长度超过实际分配时。