Учтите тот факт, что следующий код успешно компилируется и выполняется:
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<[i32]> — невозможный, бессмысленный тип, определение размера бессмысленности не очень четко определено.
Строка, которая не скомпилируется (println!("{}", std::mem::size_of::<Vec<[i32]>>()); ), имеет размер типа Vec<[i32]>, поэтому я не уверен, что понимаю вас, когда вы говорите «размер Vec не подлежит сомнению». Обратите внимание: сам факт того, что размер [i32] неизвестен во время компиляции, не может быть всей историей, поскольку он не объясняет, почему size_of::<&[i32]>() и size_of::<Box<[i32]>>() не вызывают проблем.
И Box<[i32]>, и &[i32] не бессмысленны.
@cafce25 Если бы Vec<[i32]> было бессмысленным, ошибка была бы E0747 («... предоставляется, когда ожидается тип»), а не E0277. Попробуйте, например. println!("{}", std::mem::size_of::<Vec<[]>>()); что действительно бессмысленно.
Это бессмысленно, потому что последовательность объектов неразмерного размера, выделенная в куче, не имеет смысла… Это по-прежнему допустимый синтаксис.
Если говорить более подробно о том, что сказал cafce25, то сама причина существования Vec — хранить элементы один за другим. Чтобы это работало, необходимо знать размер предметов. Вот почему Vec требует Sized, а Vec<SomethingUnsized> не компилируется. size_of здесь является лишь побочной жертвой: компилятор не знает, что size_of::<Vec<JustAboutAnything>> может вернуться 3*size_of::<usize>() для реализации такого ярлыка, он просто так не работает.

Проблема заключается в выражении типа Vec<[i32]>. Это недопустимый тип, потому что Vec<T: Sized>, но [i32]: !Sized. Это не имеет ничего общего с size_of частью этой линии.
Но, думаю, меня больше интересует более глубокая причина дизайнерского решения, которая
Vec<T>требуетTдля реализацииSized.
Ответить на этот вопрос очень просто, если задуматься о том, как работают массивы. В конце концов, Vec — это всего лишь массив динамического размера, выделенный в куче.
Массивы допускают произвольный доступ (т. е. индексацию), который осуществляется путем смещения указателя на начало массива на индекс, умноженный на размер элементов. Если не все элементы имеют одинаковый размер или этот размер неизвестен, индексирование просто не может работать. Отсюда должно быть совершенно ясно, почему Vec<[i32]> (или даже [[i32]; 4]) — бессмысленный тип.
(Если бы индексирование не было обязательным требованием, можно было бы просто помещать срезы в память один за другим, но это была бы совершенно бесполезная структура, не зная, где заканчивается каждый срез и начинается следующий. Это лучше представить с помощью единицы. размерный (сплющенный) массив/срез с отдельно поддерживаемым списком начальных индексов. Но на этом этапе вы можете просто создать массив указателей на отдельные срезы.)
Требование Sized нельзя было смягчить для T в определении структуры, даже если оно сохранялось повсюду, из-за реализации Drop: эта реализация требует выполнения действий с массивом, поэтому она требует привязки Sized; но Drop должен быть реализован либо для всех возможных экземпляров дженериков, либо ни для одного.
@jthulhu Ты прав, я это пропустил! Я удалил эту часть из своего ответа.
«Меня больше интересует более глубокая причина дизайнерского решения сделать так, чтобы Vec<T> требовал T для реализации Sized». Некоторые экземпляры
Tдолжны храниться в хранилище, выделенном в куче, поэтому размерTдолжен быть известен, чтобы выделить хранилище достаточного размера.