Pull to refresh

Comments 18

Можно подробнее про последний пример и "нормализацию"?
Что я вижу — она создаёт проблему по быстродействию. Как только вам понадобится список занятий для ученика — придётся перебирать общий список enrollments. В БД для того, чтобы делать это быстро — есть ключи и другие ухищрения, да и требования по быстродействию обычно не столь суровы, как к структурам данных в памяти, но всё равно порой разработчики сознательно идут на денормализацию базы.

Во первых, enrollments можно попробовать как-нибудь иначе организовать. В том числе, с ключами.


Во вторых, в расте всегда можно опуститься на уровень "сырых указателей" если они требуются. Да, гарантии придётся обеспечивать самостоятельно, но можно "небезопасные" внутренности завернуть в безопасный интерфейс.

«Организовать с ключами» — понятное дело, если там сколько-нибудь заметное количество элементов — мало-мальски опытный программист туда воткнёт map. Но это совсем не бесплатно (казалось бы, Rust нужен, чтобы бесплатно или задёшево давать гарантии «ссылочной целостности» и т.п.)

Насчёт «во вторых» — ну да, вроде Rc, который упомянут в статье — вроде более-менее классический weak pointer получается (один из традиционных путей решения аналогичной задачи на C++). Но почему синтаксис такой страшный? Даже страшнее, чем в C++. Нельзя ли это переписать по человечески? (нутром чую, что можно)
Опять же, автор называет это решение «сложночитаемым» (я его понимаю) и рекомендует второе, дорогое — как «более правильное».
казалось бы, Rust нужен, чтобы бесплатно или задёшево давать гарантии «ссылочной целостности» и т.п.

Язык не всемогущ. (:


Сейчас правила "заимствования" в языке довольно просты, пусть и не всегда достаточно гибки. При этом данная часть всё равно считается сложной для освоения. Честно говоря, плохо представляю как это можно было бы улучшить, чтобы и изучение не усложнить и код кучей дополнительных аннотаций не загромождать.


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


Нельзя ли это переписать по человечески? (нутром чую, что можно)

Речь о вещах типа Vec<Rc<RefCell<Class>>>? Ну можно тайпдефы использовать. А вообще мне такая матрёшка даже нравится: оно гибче, чем заводить отдельные классы на каждую комбинацию возможностей.

вообще тут проблема на мой взгляд в том что нет неразрывных конструкций

в 3 случае мы имеем 2 взаимосвязанных delete в принципе тут мог решить проблему алгоритм разрешения циклических ссылок аналогичный алгоритму сборки муссора
для этого delete пришлось бы объединить в атомик (внутри атомика компилятор допускает висящие ссылки, но не допускает их использования) думаю к этому придут

в 1 случае компилятору ничто не мешает увидеть, что область использования переменной jill_ref_mut заканчивается после строки jill_ref_mut.celebrate_birthday(); тут компилятор тоже может без проблем разобраться однозначно сам.

аналогично случай 2.

по логике, у ученика должна быть ссылка на школу учеником которой он является, как тут быть?
«по логике» — достаточно сомнительное обоснование наличия ссылки у ученика.
Как вариант, индекс или id вполне могут выполнять роль «ссылки». Всё зависит от задачи, а задачи могут со временем меняться.
у меня есть ученик, мне нужно получить школу учеником которой он является, как? понятное дело без каких либо глобальных списков школа — ученик
А куда будет указывать ссылка?
Ссылку можно сделать, если известно, что объект «ученик» будет жить меньше, чем объект «школа».
Односторонние ссылки в этом случае проблемы не составляют.
У индекса/id есть преимущество — он не прибивает объект, на который ссылается, гвоздями к определённому месту в памяти.
А куда будет указывать ссылка?

по логике, на полноценный объект школа.

я в Rustе не разбираюсь, вот и спрашиваю, раз пропагандируется отказ от циклических ссылок значит должна быть вменяемая альтернатива, глобальные (самопальные!!!) списки к этому не относятся.
Вариант, когда нужны взаимные ссылки, предложен в статье — «владеющая ссылка».
К сожалению в статье не упомянут другой недостаток Rc/Arc — при неправильном употреблении они могут привести к утечке памяти.
Из взаимных ссылок можно выделить специальный вид, назовем — «неотъемлемого владения», например школа-ученик, ученик без школы в принципе существовать не может, когда, гм, персона переходит из одной школы в другую создается новый объект ученик. По логике, стандартизация в языке такого вида ссылок, без костылей типа Rc, закрыло бы львиную долю случаев, как-то так.
Хорошо, в данном случае вполне можно поместить школу владеющую списком учеников в Rc, а ученикам дать Weak ссылку на школу. Это вполне будет работать.

Но это сразу станет немного «дороже» — часть проверок перенесётся в рантайм, в программе придётся обрабатывать случаи, когда «что-то пошло не так».
У всего своя цена, серебряных пуль нет.

Поэтому в первую очередь ставится вопрос: зачем эта обратная ссылка на школу? Нужна ли она? Можно ли обойтись без неё? Какая стоит задача?

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

Чуть выше я предложил использовать индексы или id — это не то же самое, что список пар «ученик-школа», упомянутый в статье. Тут уместнее предположить две владеющих структуры списком школ и списком учеников, а школы и ученики могут, если нужно, просто хранить номера или ключи в этой структуре.

Недостатки ссылок вполне перечислены в статье — ссылка может быть либо одна изменяемая, либо много — на неизменяемый объект. Меньше ссылок, связывающих состояние — меньше ограничений. Преимущество ссылки — более быстрое получение объекта при необходимости и статические гарантии, что объект существует.

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

Как компромис в такой ситуации можно использовать {Hash,BTree}Map с идентификаторами, учитывая, что получить из него что-то по ключу — это уже более дорогая операция, чем обычный индекс. Так же без гарантий и с лишними проверками и рисками.

Rust, как системный язык, даёт разные инструменты для тонкого контроля над тем, как работает программа. При этом инструменты далеко не всегда взаимозаменяемые, каждый со своими требованиями, ограничениями, ценой, хотя может показаться «здесь куча разных способов сделать одно и то же». Поэтому ещё раз повторюсь: в первую очередь всё определяет задача, а уже дальше нужно думать, какой инструмент для её решения использовать. Делать что-то «для удобства», «так привычно» или «на всякий случай» — плохая мотивация.
Вы так пишите как будто получение по ученику — школы что-то невероятное, хотя это типовая ситуация, ладно, расширим модель, у нас есть объект (структура в данном случае) — «ГородскаяОлимпиада» со списком объектов «УчастникОлимпиады», очевидно (мне), «олимпиада» должна знать из какой школы ученик, статистику по школам вести, например, да хотя-бы куда поздравления слать, да можно обойтись двумя ссылками (в «УчастникОлимпиады») — ученик и школа, но это несерьезно, мало того что надо постоянно согласовывать (вручную) чтоб ученик действительно был из заданной школы (при этом в совершенно постороннем объекте!), так и в случае если надо расширить набор полей, надо их добавлять не в, или, не только в ученика, а в, опять-же, совершенно посторонний объект, если-же в «УчастникОлимпиады» будет ссылка на ученика а нем на школу, по сути, будет тоже самое, только памяти жрать меньше и управлять правильнее.
UFO just landed and posted this here

Первый пример прекрасно компилируется. Что я делаю не так?


fn main() {
    let mut jill = Person::new("Jill", 19);
    let jill_ref_mut = &mut jill;
    jill_ref_mut.celebrate_birthday();
    println!("{}", jill.name()); // невозможно заимствовать jill как неизменяемое
                                 // потому что это уже заимствовано как
                                 // изменяемое
}

(rustc 1.31.1)

Вы используйте современный Rust, где уже ввели NLL (non-lexical lifetimes)
Sign up to leave a comment.

Articles