Почему `std::mem::size_of::<Vec<[i32]>>()` не компилируется?

Учтите тот факт, что следующий код успешно компилируется и выполняется:

println!("{}", std::mem::size_of::<Vec<i32>>()); // 24
println!("{}", std::mem::size_of::<Vec<char>>()); // 24
println!("{}", std::mem::size_of::<Vec<&str>>()); // 24
println!("{}", std::mem::size_of::<Box<i32>>()); // 8
println!("{}", std::mem::size_of::<Box<char>>()); // 8
println!("{}", std::mem::size_of::<Box<[i32]>>()); // 16
println!("{}", std::mem::size_of::<&[i32]>()); // 16
println!("{}", std::mem::size_of::<*mut *const [i32]>()); // 8

Теперь рассмотрим следующий код:

println!("{}", std::mem::size_of::<[i32]>());

Это не удается скомпилировать из-за ошибки E0277, в которой сообщается, что размер [i32] неизвестен во время компиляции. Я определенно понимаю, почему это должно быть так, потому что [i32] — это срез i32 (т. е. «необработанный» срез в смысле, данном здесь [1], а не указатель на срез (например, &[i32]), размер которого известен во время компиляции ( то есть размер жирного указателя = 2 * size_of(usize))).

Но теперь рассмотрим следующий код:

println!("{}", std::mem::size_of::<Vec<[i32]>>());

Когда вы пытаетесь скомпилировать этот код, вы также получаете ошибку E0277, в которой сообщается, что размер [i32] неизвестен во время компиляции.

Почему это так? Рассмотрим еще раз эти строки сверху:

println!("{}", std::mem::size_of::<Vec<i32>>()); // 24
println!("{}", std::mem::size_of::<Vec<char>>()); // 24
println!("{}", std::mem::size_of::<Vec<&str>>()); // 24

Мы ясно видим, что размер Vec<T> не зависит от размера T, но всегда представляет собой размер структуры с 3 полями — указатель на данные (8), длина (8), емкость (8) — следовательно, 24.

Так почему же std::mem::size_of::<Vec<[i32]>>() просто не возвращает 24, а не компилируется?

Я знаю, что Vec<T> требует T для реализации Sized, и это непосредственная причина. Но, думаю, меня больше интересует более глубокая причина дизайнерского решения, согласно которому Vec<T> требует T для реализации Sized.

Спасибо!

[1] https://doc.rust-lang.org/reference/type-layout.html#slice-layout

«Меня больше интересует более глубокая причина дизайнерского решения сделать так, чтобы Vec<T> требовал T для реализации Sized». Некоторые экземпляры T должны храниться в хранилище, выделенном в куче, поэтому размер T должен быть известен, чтобы выделить хранилище достаточного размера.

prog-fh 15.06.2024 12:19
Vec<[i32]> — невозможный, бессмысленный тип, определение размера бессмысленности не очень четко определено.
cafce25 15.06.2024 12:26

Строка, которая не скомпилируется (println!("{}", std::mem::size_of::<Vec<[i32]>>()); ), имеет размер типа Vec<[i32]>, поэтому я не уверен, что понимаю вас, когда вы говорите «размер Vec не подлежит сомнению». Обратите внимание: сам факт того, что размер [i32] неизвестен во время компиляции, не может быть всей историей, поскольку он не объясняет, почему size_of::<&[i32]>() и size_of::<Box<[i32]>>() не вызывают проблем.

yg-i 15.06.2024 12:29

И Box<[i32]>, и &[i32] не бессмысленны.

cafce25 15.06.2024 12:31

@cafce25 Если бы Vec<[i32]> было бессмысленным, ошибка была бы E0747 («... предоставляется, когда ожидается тип»), а не E0277. Попробуйте, например. println!("{}", std::mem::size_of::<Vec<[]>>()); что действительно бессмысленно.

yg-i 15.06.2024 12:38

Это бессмысленно, потому что последовательность объектов неразмерного размера, выделенная в куче, не имеет смысла… Это по-прежнему допустимый синтаксис.

cafce25 15.06.2024 12:40

Если говорить более подробно о том, что сказал cafce25, то сама причина существования Vec — хранить элементы один за другим. Чтобы это работало, необходимо знать размер предметов. Вот почему Vec требует Sized, а Vec<SomethingUnsized> не компилируется. size_of здесь является лишь побочной жертвой: компилятор не знает, что size_of::<Vec<JustAboutAnything>> может вернуться 3*size_of::<usize>() для реализации такого ярлыка, он просто так не работает.

user4815162342 15.06.2024 21:10
Почему Python в конце концов умрет
Почему Python в конце концов умрет
Последние 20 лет были действительно хорошими для Python. Он прошел путь от "просто языка сценариев" до основного языка, используемого для написания...
0
7
103
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Проблема заключается в выражении типа Vec<[i32]>. Это недопустимый тип, потому что Vec<T: Sized>, но [i32]: !Sized. Это не имеет ничего общего с size_of частью этой линии.

Ответ принят как подходящий

Но, думаю, меня больше интересует более глубокая причина дизайнерского решения, которая Vec<T> требует T для реализации Sized.

Ответить на этот вопрос очень просто, если задуматься о том, как работают массивы. В конце концов, Vec — это всего лишь массив динамического размера, выделенный в куче.

Массивы допускают произвольный доступ (т. е. индексацию), который осуществляется путем смещения указателя на начало массива на индекс, умноженный на размер элементов. Если не все элементы имеют одинаковый размер или этот размер неизвестен, индексирование просто не может работать. Отсюда должно быть совершенно ясно, почему Vec<[i32]> (или даже [[i32]; 4]) — бессмысленный тип.

(Если бы индексирование не было обязательным требованием, можно было бы просто помещать срезы в память один за другим, но это была бы совершенно бесполезная структура, не зная, где заканчивается каждый срез и начинается следующий. Это лучше представить с помощью единицы. размерный (сплющенный) массив/срез с отдельно поддерживаемым списком начальных индексов. Но на этом этапе вы можете просто создать массив указателей на отдельные срезы.)

Требование Sized нельзя было смягчить для T в определении структуры, даже если оно сохранялось повсюду, из-за реализации Drop: эта реализация требует выполнения действий с массивом, поэтому она требует привязки Sized; но Drop должен быть реализован либо для всех возможных экземпляров дженериков, либо ни для одного.

jthulhu 19.06.2024 18:18

@jthulhu Ты прав, я это пропустил! Я удалил эту часть из своего ответа.

FZs 21.06.2024 10:49

Другие вопросы по теме