Pull to refresh

Comments 12

Отличная новость, больше тулчейнов хороших и разных для хорошего языка! А пока есть небольшая проблема с компиляцией rustc для ARM: для сборки используется помимо g++ некий snapshot, а он предоставляется только для x86 архитектур. Может кто-то подсказать как это обойти? Я в курсе, что официально пока что поддерживаются только x86, но кого это когда-то останавливало?)
В гугле удалось найти только про кросскомпиляцию программ с использованием --target и указанием различных тулчейнов.
UFO just landed and posted this here
А можно подробнее узнать про «типы с динамическим размером»? Что это такое, в чем особенности реализации, какие задачи решают и т.д.
Я так понимаю это не то же самое что обычные динамические структуры данных вроде списков и векторов?
У меня ощущение, что это подразумевается своеобразный полиморфизм этапа выполнения, когда структура (декларация) класса могут меняться в процессе работы, соответственно и размер объектов будет меняться. Меняется размер, наверняка, с помощью какого нибудь метода «эволюция» или нечто подобного.
UFO just landed and posted this here
А что следует понимать под «размером типа»? Размер объекта этого типа или что-то другое?
Можно ли сказать, что в C++ размер объектов std::vector, std::list и т.п. также неизвестен во время компиляции? (я бы так сказал, но вдруг в терминах Rust под этим понимается что-то другое...)
Если да — то что нового привнес Rust в работу с такими динамическими структурами данных, по сравнению с тем же С++?
UFO just landed and posted this here
Ну хорошо, в случае std::vector там тоже где-то будет указатель на начало и дополнительная информация — указатель на последний элемент, на конец буфера и еще что-то. В случае std::list — указатель на начало и дополнительная информация — указатель на следующий элемент списка.
Все-же как-то все очень непонятно, что именно они там с этими «типами с динамическим размером» сделали.
stepik777 всё правильно объясняет.

В большинстве языков, в частности, и в Rust, типы так или иначе имеют фиксированный размер, который известен компилятору. Примитивные типы, конкретные структуры, фиксированные массивы и прочее — их размер, т.е. необходимое для них число байт, всегда известны.

С другой стороны, существуют типы, размер которых на этапе компиляции неизвестен точно. Например, это «абстрактный» массив — т.е. некоторая последовательность элементов одного типа, но с неизвестной длиной. Другой пример — тип, объединяющий все типы, реализующие какой-нибудь трейт. Например, если Display — это трейт, то Display в контексте типа обозначает любой тип, реализующий этот трейт. Понятно, что потенциально любой тип может реализовывать трейт, поэтому размер таких «объединяющих» типов тоже неизвестен.

Типы с неизвестным размером можно использовать только при выполнении двух условий:
  1. доступ к их объектам осуществляется только через указатель;
  2. вместе с указателем должна храниться какая-то дополнительная информация, позволяющая «обойти» статическое незнание конкретики о типе.


Первое условие нужно, потому что вы не можете использовать объекты типа с неизвестным размером по значению — для этого компилятору придётся выделить память на стеке, но как её выделить, если нужный размер неизвестен? Размер же указателя известен всегда.

Второе условие нужно, потому что, не зная дополнительной информации о конкретном значении, вы не сможете его использовать. Например, в случае с массивом вам необходимо знать его длину, иначе вы не сможете безопасно получить элемент по его индексу. В случае с трейт-объектом вам нужно знать, метод из какой именно реализации трейта необходимо вызвать. В случае с массивами дополнительная информация — это длина массива, а в случае с трейт-объектами — это таблица виртуальных методов. В Rust эта информация хранится непосредственно в объекте-ссылке/указателе, поэтому ссылки/указатели на типы с динамическим размером в Rust называются fat pointers, толстыми указателями.

Например, возьмём тип &[i32] — срез i32. Это ссылка на тип с динамическим размером [i32]. Обычно ссылки реализуются в виде указателей, например, &i32 на самом низком уровне представляется в виде одного 8-байтного или 4-байтного числа. Но &[i32] — это толстый указатель, и он состоит из двух чисел: указателя на начало массива и длины этого массива. При создании &[i32], т.е. при взятии ссыли на массив и преобразовании её в срез, информация о длине исходного массива сохраняется внутри толстого указателя.

&Display — это трейт-объект для трейта Display. В переменной такого типа может храниться указатель на любой тип, который реализует трейт Display. Однако для того, чтобы такую переменную можно было бы использовать, нужно вместе с указателем на значение хранить ссылку на таблицу методов Display, которые реализует тип, значение которого лежит в этой переменной. Поэтому &Display — это толстый указатель, который состоит из указателя на значение и указателя на таблицу виртуальных методов для типа этого значения. При создании ссылки на объект и преобразовании её в трейт-объект указатель на таблицу виртуальных методов сохраняется внутри толстого указателя.

В модуле std::raw лежат структуры, описывающие толстые указатели для разных видов DST. Эти типы можно напрямую преобразовывать (с помощью mem::transmute(), аналога reinterpret_cast из C++) в ссылки на типы с динамическим размером, т.е., например, можно преобразовать std::raw::Slice в &[i32] и наоборот.

Что касается изменений именно в данном релизе, то теперь можно сказать, что типы с динамическим размером поддерживаются компилятором (практически) в полном объёме. Например, можно создавать умные указатели, аналогичные Box из стандартной библиотеки, которые будут прозрачно работать с DST, т.е. автоматически становиться толстыми указателями, если они используются для DST. Раньше этого было сделать нельзя.
То есть по сути это «толстые указатели» — на самом деле обычные структуры/классы, которые ведут себя как указатели и содержат дополнительную информацию? Некий аналог умных указателей С++, с какой-то поддержкой со стороны компилятора?
Может ли программист придумать свой DST? Будет ли компилятор понимать что это «DST» и работать с ним тем особым образом, как он работает со встроенными?
Это не совсем «обычные структуры/классы», знание о них заложено в компилятор. Например, в случае с трейт-объектами это не обойти никак, потому что в трейт-объектах дополнительная информация — это указатель на vtable, который знает только компилятор. Я думаю, что, теоретически, срезы, которые содержат дополнительную информацию о длине, можно было бы реализовать в сторонней библиотеке, но на данный момент срезы также встроены в язык. То, что лежит в std::raw — это совместимое представление толстых указателей в виде структур, но это не значит, что эти структуры на самом деле используются.

Да, программист может определять свои DST. Например, Path и CStr из стандартной библиотеки — это типы с динамическим размером. Однако на данный момент возможности по созданию DST сильно ограничены, и их реализация не очень, скажем так, приятна глазу. По факту, можно создавать и использовать срезоподобные DST (как вышеописанные Path и CStr). Возможно, можно как-то заюзать этот механизм для сохранения собственной информации. Чего-то большего пока что сделать нельзя, например, нельзя как-то описать толстый указатель с дополнительной информацией в виде собственной структуры, но, я думаю, кто-нибудь может написать RFC по этому поводу, и, возможно, что-то такое будет в будущем добавлено в язык. Пока что, насколько я понимаю, особой необходимости в этом нет.
UFO just landed and posted this here
Sign up to leave a comment.

Articles