> [i]чисто механически, следуя определённым правилам программирования, типа "не использовать
> unsafe"[/i] - не могу осознать, каким образом unsafe связан c состоянием
> гонки в данном случае?
> я не знаю rust и интересно, каким образом он помогает избегать состояний
> гонки.Я сейчас потратил полчаса пытаясь изобрести краткое объяснение. Мне не удалось это. Там вся магия сокрыта в системе типов с лайфтаймами и borrowing'е, и мне кажется, что единственный способ понять как это работает -- это взять и попытаться создать race condition в rust'е. Если потратить несколько дней на борьбу с borrow checker'ом, пробуя объехать его на кривой козе и так, и эдак, то в голове начинает зарождаться понимание, почему race condition невозможен. Если потратить ещё месяц на то, чтобы читать статьи и чужой код, то можно понять как, несмотря на всё это, возможно писать какой-нибудь рабочий и полезный код. Но так или иначе, чтобы понять как это работает, надо рассмотреть десяток примеров с потенциальным race condition, разбирая каждый на предмет "а если попытаться так, то ничего не получиться потому-то и потому-то", и завершая это сентенцией типа "таким образом у нас есть такие-то пути справится с проблемой, этот лучше в таких-то ситуациях, а этот в таких-то".
Но чтобы не звучать совсем абстрактно, я приведу один пример. Если у меня есть
struct Dummy {
a: u32,
b: u32,
}
Допустим я хочу чтобы для этой структуры всегда выполнялся бы какой-нибудь инвариант, например, a = 2*b.
Допустим, я напишу функцию:
fn double_this_dummy(dummy: &mut Dummy) {
dummy.a *= 2;
dummy.b *= 2;
}
Инвариант сохраняется. Можно предположить, что параллельный поток в это время изменит именно этот объект, вот ровно между удвоением a и b он возьмёт и уменьшит a на 3 и b на 6. Тогда инвариант будет нарушен. Но rust не позволит мне создать две mutable ссылки на этот объект, поэтому если внутри этой функции у меня есть ссылка на dummy, то у параллельного потока её не будет. А значит никакого race condition'а не случится.
Если мы попытаемся таки создать две mutable ссылки на один и тот же объект, так чтобы одна была в одном потоке, а другая в другом, то это выльется в долгий сеанс переругивания с borrow-checker'ом, и кончится либо использованием unsafe кода, либо осознанием своей глупости и нахождением safe пути достичь наших целей. Сложно объяснить как это происходит -- это надо испытать на себе.