Zig добавляет блокировку указателей для ArrayList
Zig: Pointer Stability for ArrayLists
В Zig появилась защита от ошибок, связанных с нестабильностью указателей в ArrayList. Новая функция lockPointers() блокирует изменение внутреннего буфера, предотвращая ситуации, когда указатели на элементы становятся недействительными после перераспределения памяти. В статье показан пример, где такая ошибка приводит к порче данных, и объясняется, как блокировка помогает обнаружить проблему на раннем этапе. Также рассматриваются особенности, связанные с упорядоченностью ArrayList.
Проблема в том, что элементы Context.lines.items зависят от местоположения Context.history.items, но это местоположение может измениться, если Context.history потребуется вырасти за пределы текущей ёмкости.
- amluto
Это выглядит слабо.
В таком языке, как Rust, компилятор сам «блокирует» указатели за вас, и вы не можете забыть об этом.
В таком языке, как C++ (и, предположительно, Zig), можно было бы, по крайней мере теоретически, сделать так, чтобы итераторы и срезы, ссылающиеся на хранилище динамического массива, содержали некий вид блокировки, которая закрепляет это хранилище.
Но этот API требует, чтобы программист помнил о необходимости блокировать указатели, а также требует, чтобы программист удерживал блокировку в течение правильной области кода. И мне кажется, что даже в примере из поста в блоге блокировка взята полностью вне функции, которая требует стабильности, так что нет ничего, что могло бы правильно ограничить область блокировки. Даже система типов не может помочь — проблемная функция parse не может объявить, что она хочет параметр ArrayList с заблокированными указателями.
- _bohm
Это хорошая функция, но я не могу избавиться от ощущения, что если вам нужен стабильный указатель на элемент коллекции, то ArrayList — неправильная структура данных для этого? Может, кто-то объяснит, когда вы бы использовали это вместо, например, простого хранения индекса. Альтернативно, можно использовать Unrolled Linked List (ранее известный как SegmentedList в Zig до его удаления в 0.16, не знаю почему).
- portly
Это имеет большой смысл, если учесть, что это согласуется с остальной частью языка. Это еще один способ установить растяжки в вашем коде, чтобы поймать свои собственные ошибки программирования. Похоже на использование assert'ов в функциях для проверки ввода и вывода.
Я часто использую ArrayList, так что рад добавить это по всему коду, чтобы укрепить его.
Могу представить, что это не всем по вкусу, но тогда вам, вероятно, не понравится и любая другая явность.
- Rendello
Кстати: одна особенность синтаксиса Zig, которую я действительно упускаю в Rust, показана во втором блоке кода, а именно многострочные строковые литералы с префиксом, например:
const text =
\\Это длинный комментарий
\\Но я могу разбить его на строки произвольно
\\И сохранить мой отступ.
;
Я начал использовать библиотеку макросов Rust `docstr` [1], которая делает то же самое:
const TEXT: &'static str = docstr!(
/// Теперь я могу делать это и в Rust.
/// Я предпочитаю этот стиль во многих случаях
/// для длинных текстов.
);
Она даже работает с макросами (пример из документации):
let greeting: String = docstr!(format!
/// Привет, меня зовут {name}.
/// Мне {} лет!
age
);
- boricj
Мне потребовалась минута, чтобы понять, что это проверяет изменение указателя внутри контейнера, а не блокирует/разблокирует структуру данных, как поверхность SDL.
Недавно я реализовал пользовательский контейнер на C++ для пути, компоненты которого можно было перебирать, основанный на std::string. Я просто храню индексы и ссылку на строку, так что мои итераторы не инвалидируются, если std::string перераспределяется после изменения. Гораздо менее подвержено ошибкам за небольшую дополнительную стоимость.