Is & [T] буквально является псевдонимом Slice в ржавчине?

&[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.

Это правильно?

Означает ли это, что в целом допустимо вернуть a &[T] до тех пор, пока действительный внутренний указатель данных действителен, независимо от объема оригинала &[T], который возвращается, поскольку назначение &[T] операция копирования?

(Мне кажется, что это чрезвычайно противоречиво интуитивно... поэтому, возможно, я недопонимаю, если я прав, если два &[T], указывающие на одни и те же данные, не могут быть действительными, потому что они не будут синхронизировать длины если вы измените один...)

Ответы

Ответ 1

std::raw::Slice &[T], как вы заметили, "эквивалентен" структуре std::raw::Slice. На самом деле, Slice является внутренним представлением значения &[T], и да, это указатель и длина данных за этим указателем. Иногда такая структура называется "указателем жира", то есть указателем и дополнительной информацией.

Когда вы передаете значение &[T], вы действительно просто копируете его содержимое - указатель и длину.

Если это был недопустимый указатель на переменную "slice", значение len в выражении println() было бы 0, но это не так. Настолько эффективно копия не только значения указателя, но и полная копия структуры Slice. Это правильно?

Так, да, точно.

Означает ли это, что в целом допустимо вернуть a & [T] до тех пор, пока действительный внутренний указатель данных действителен, независимо от объема оригинала & [T], который возвращается, поскольку назначение & [T] операция копирования?

И это также верно. То, что вся идея заимствованных ссылок, в том числе заимствованные фрагменты, статически проверяется на использование, пока их референт жив. Когда DST наконец приземлится, фрагменты и регулярные ссылки будут еще более унифицированы.

(Мне кажется, что это чрезвычайно противоречиво интуитивно... поэтому, возможно, я недопонимаю, если я прав, если два и [T], указывающие на одни и те же данные, не могут быть действительными, потому что они не будут синхронизировать длины если вы измените один...)

И это на самом деле абсолютно актуальная проблема; это одна из проблем с псевдонимом. Однако Rust предназначен для предотвращения таких ошибок. Есть две вещи, которые делают наложение разрезов действительными.

Во-первых, срезы не могут изменить длину; нет методов, определенных на &[T] которые позволят вам изменить его длину на месте. Вы можете создать производный срез из среза, но это будет новый объект.

Но даже если срезы не могут изменить длину, если данные могут быть мутированы через них, они все равно могут привести к катастрофе, если будут сглажены. Например, если значения в срезах являются экземплярами перечисления, изменение значения в таком псевдонимом фрагменте может сделать указатель на внутренности значения перечисления, содержащиеся в этом фрагменте, недействительными. Итак, во-вторых, Rust aliasable slices (&[T]) неизменяемы. Вы не можете изменять значения, содержащиеся в них, и вы не можете принимать в них изменчивые ссылки.

Эти две функции (и проверки компилятора на время жизни) делают сглаживание срезов абсолютно безопасным. Однако иногда вам нужно изменить данные в срезе. И тогда вам нужен измененный фрагмент, называемый &mut [T]. Вы можете изменить свои данные с помощью такого фрагмента; но эти фрагменты не являются алиасируемыми. Вы не можете создать два изменяемых фрагмента в одну и ту же структуру (например, массив), поэтому вы не можете сделать ничего опасного.

Обратите внимание, однако, что использование transformute transmute() для преобразования среза в Slice или наоборот является небезопасной операцией. &[T] гарантированно статически корректно, если вы создаете его с использованием правильных методов, например, вызываете as_slice() на Vec. Однако, создавая его вручную с помощью структуры Slice а затем трансформируя его в &[T], подвержено ошибкам и может легко segfault ваша программа, например, когда вы назначаете ее больше длины, чем на самом деле распределено.