Как Rust обрабатывает преобразование & SizedType в & UnsizedType? - PullRequest
1 голос
/ 05 мая 2019
struct MaybeSized<T: ?Sized> {
    v: T,
}

fn main() {
    let sized = MaybeSized {
        v: "sized".to_string(),
    };

    use std::fmt::Display;

    {
        // what exactly happens here?
        let ref1: &MaybeSized<Display> = &sized;
    }
    {
        // why this fails to compile?
        let ref2: &MaybeSized<str> = &sized;
    }
}

MaybeSize<String> - размерный тип;что находится в стеке и куче при создании ref1 : &MaybeSized<Display>)?Почему эта «магия» не работает для другого типа без размера, например MaybeSized<str>?

1 Ответ

2 голосов
/ 05 мая 2019

Преобразование из &MaybeSized<String> в &MaybeSized<dyn Display> называется нестандартным принуждением .Конкретные типы могут быть преобразованы в объекты признаков для реализуемых ими признаков, и это принуждение распространяется на общую структуру при определенных условиях:

Foo<..., T, ...> до Foo<..., U, ...>, когда:

  • Foo является структурой.
  • T реализует Unsize<U>.
  • Последнее поле Foo имеет тип, включающий T.
  • Если это поле имеет тип Bar<T>, то Bar<T> реализует Unsized<Bar<U>>.
  • T не является частью типа любых других полей.

(Для получения полной информации перейдите по ссылке на ссылку на язык выше.)

Переменная sized хранится в стеке, но данные String размещаются в куче.Ссылка ref1 хранится в стеке как жирный указатель - указатель на sized вместе с указателем на таблицу виртуальных методов для String as dyn Display, чтобы позволить динамический вызов нужных методов при необходимости.

Это не сработает для MaybeSized<str>, потому что не существует нестандартного принуждения, которое преобразуется в str.Вы можете конвертировать и &String в &str, используя разыменное принуждение , но это не то, что нам здесь нужно - для изменения размера типизированного типа требуется несогласованное приведение.Тип MaybeSized<str> без размера состоит из фактических строковых данных , в то время как MaybeSized<String> состоит из длины, емкости и указателя на кучу, поэтому нет никакого способа, чтобы макеты памяти соответствовали.

Существуют и другие случаи, которые работают, хотя, например,

let a = MaybeSized { v: [65u8, 66, 67]};
let b: &MaybeSized<[u8]> = &a;

работает нормально, так как существует неравномерное приведение от [u8; 3] до [u8].С небезопасным кодом вы можете преобразовать это в &MaybeSized<str>, если вы действительно хотите:

let c: &MaybeSized<str> = unsafe { &*(b as *const _ as *const _) };

Я не могу придумать безопасный способ создания &MaybeSized<str>.

( код на детской площадке )

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...