В примерах статьи передача идёт не по ссылке, а по значению (передача владения) - это и обсуждаем. При передаче по значению используется move-семантика по-умолчанию. Либо полное копирование объекта если тип реализует Copy.
Тем не менее, между правилами языка, обязательными к соблюдению, и линтерами, делающими анализ по поиску потенциально опасных участков кода, всё же есть существенная разница.
в этом случае как будут работать эти механизмы “владения”?
Точно так же, как и в однопотоке. Правила не меняются. Любой ресурс (переменная) имеет в один момент времени:
единственного владельца
либо N читателей, либо 1 писатель
Для одновременного доступа к переменным из разных потоков используются мьютексы, которые в рантайме проверяют правило “exactly one writer XOR many readers”.
и в С можно же ограничить видимость переменной (того же буфера), написав слово static, это не будет эквивалентом владения в rust?
Нет, не будет. Код в пределах видимости этой переменной может по-прежнему делать всякое. Классический пример - попытка добавить в массив (allocable vector \ C# List) нового элемента во время итерации по этому массиву. C# выбросит исключение в рантайме при такой попытке. В C\C++ такое скомпилируется, но это будет UB. Rust откажется такой код компилировать в принципе.
let mut array = vec![1, 2, 3, 4];
for item in &array {
println!("item: {item}");
if *item == 4 {
array.push(5); // forbidden
}
}
buffer, очевидно, предполагается с move-семантикой (то есть оно не реализует трэйт Copy) так как мы хотим получить этот буффер во владение, а при вызове wait() - отдать владение обратно вызывающему.
и задумываешься о качестве того, как вы формулируйте свои промты
Тут ещё неплохо было бы уточнять, с какими LLM имеется опыт. Я уже сталкивался с таким же: человечек упорно стоял на том, что LLM ничего не могут и делают неправильно почти всегда. В конце концов выяснилось, что он использовал какую-то локальную LLM, а не платную версию LLM вроде тех что предоставляют Cursor/Claude/etc :\
Это стандартный подход в расте: прячем все unsafe-операции за безопасным API, которое будет проверять входные данные и убеждаться в том, что никакие инварианты нарушены не будут. Ничего лучшего вы придумать не сможете, если хотите достичь заявленных целей в статье.
Но данная оптимизация в принципе редко применима на практике, это правда. В прикладных программах, очевидно, так никто делать не станет: код становится слишком сложнее. Цена за выигрыш слишком высока, всем плевать на лишние 300кб.
Автор избавился от panic добавив assert_unchecked, что определённо хуже, т. к. последнее в случае ошибки в коде тихо завалит или попортит программу.
Так ошибки не будет в данном коде никогда. Вставка assert_unchecked - это декларация инварианта для структуры S. Структуру S мы можем создать только одним способом - через вызов S::new, который проверяет на корректность входные данные.
Но и он внезапно 30 сентября слился с Гитхаба, перенеся разработку на свой сайт.
Как я понял, к нему в issue понабежали "советчики" как правильно готовить гит, а dotexe1337 это вообще не интересно - он просто пилит vxkex для себя, не позиционируя себя в качестве "professional programmer". Он гитхаб использовал только в качестве системы тикетов об ошибках и облачного хранения кода (а-ля дропбокс). Я думаю, это и является единственной причиной почему проект был заархивирован на гитхабе.
Да, это известный баг. Занятно, что есть простой способ это обойти. После сохранения надо тут же ещё раз сохранить игру в тот же самый файл. После этого простого действия, при загрузке ход пропускаться не будет.
Потому что правила автовывода лайтаймов (lifetime elision) строго определены, и данный случай под эти правила не подходит.
Вот пример кода, где выставление одинакового лайфтайма (времени жизни, ВЖ) во всех местах в функции foo_incorrect не пройдет проверку БЧ (borrow checker):
// https://play.rust-lang.org/?version=stable&mode=debug&edition=2024&gist=1ce9cb09c241bf12fba2bd33574b24b0
fn main() {
let mut str_mut = "";
let s1 = String::from("str1");
{
let s2 = String::from("str2");
str_mut = foo_incorrect(&s1, &s2); // fail here
// str_mut = foo_correct(&s1, &s2); // will be ok
}
println!("{str_mut}");
}
fn foo_correct<'long, 'short>(a: &'long str, b: &'short str) -> &'long str {
todo!()
}
fn foo_incorrect<'a>(a: &'a str, b: &'a str) -> &'a str {
todo!()
}
Надо понимать, что БЧ выполняет проверку ВЖ для каждой функции отдельно, не заглядывая в тела других использующихся функций. То есть, в примере выше - неважно что будет написано в теле функций foo_correct \ foo_incorrect - проверка ВЖ для функции main от этого никак не изменится: учитываться будут только сигнатуры вызываемых функций.
Если бы для каждой функции анализировались тела всех используемых функций, то время компиляции стремилось бы к бесконечности. Но что хуже - такое поведение вносило бы гораздо больше ошибок из-за неправильного вывода ВЖ. Даже стандартные правила вывода ВЖ иногда приводят к неверному результату, приходится вручную указывать (но это редко).
Компилятор не может знать что вы хотите написать, он может только проверить на корректность написанное.
Управление памятью реализовано через владение (ownership). Компилятор на этапе _компиляции_ анализирует код и сам вставляет вызовы free (условно) в то место, где владелец данных (переменная) выходит из скоупа (то есть гарантированно больше никто не может ссылаться на данные).
Контейнеры с подсчётами ссылок есть, но используются не часто.
Всю статью не читал, осилил только первый наброс про мутабельность, и вот это правильный комментарий.
Ссылки в расте надо интерпретировать именно так:
&mut T - уникальная ссылка. Из этого свойства как раз и вытекает возможность безопасной мутации объекта.
&T - шаренная ссылка. На этот объект может иметь доступ много кто, поэтому в общем случае изменять данные под ней нельзя. Но можно переложить ответственность за контроль корректного доступа (exactly one writer XOR multiple readers) в рантайм с помощью Mutex, RwLock или иных способов Interior Mutability.
К сожалению уже устоявшаяся классическая дискриминация по признаку наличия\отсутствия смартфона. Типа, если нету у тебя смартфона, то ты и не человек вовсе.
В примерах статьи передача идёт не по ссылке, а по значению (передача владения) - это и обсуждаем. При передаче по значению используется move-семантика по-умолчанию. Либо полное копирование объекта если тип реализует
Copy.Тем не менее, между правилами языка, обязательными к соблюдению, и линтерами, делающими анализ по поиску потенциально опасных участков кода, всё же есть существенная разница.
Точно так же, как и в однопотоке. Правила не меняются. Любой ресурс (переменная) имеет в один момент времени:
единственного владельца
либо N читателей, либо 1 писатель
Для одновременного доступа к переменным из разных потоков используются мьютексы, которые в рантайме проверяют правило “exactly one writer XOR many readers”.
Нет, не будет. Код в пределах видимости этой переменной может по-прежнему делать всякое. Классический пример - попытка добавить в массив (allocable vector \ C# List) нового элемента во время итерации по этому массиву. C# выбросит исключение в рантайме при такой попытке. В C\C++ такое скомпилируется, но это будет UB. Rust откажется такой код компилировать в принципе.
В статье на C указывают код, аналог которого реализуют:
buffer, очевидно, предполагается с move-семантикой (то есть оно не реализует трэйтCopy) так как мы хотим получить этот буффер во владение, а при вызовеwait()- отдать владение обратно вызывающему.Тут ещё неплохо было бы уточнять, с какими LLM имеется опыт. Я уже сталкивался с таким же: человечек упорно стоял на том, что LLM ничего не могут и делают неправильно почти всегда. В конце концов выяснилось, что он использовал какую-то локальную LLM, а не платную версию LLM вроде тех что предоставляют Cursor/Claude/etc :\
"цитата" может быть получена из того же /dev/urandom
Я генерирую пароли этой командой:
Это стандартный подход в расте: прячем все unsafe-операции за безопасным API, которое будет проверять входные данные и убеждаться в том, что никакие инварианты нарушены не будут. Ничего лучшего вы придумать не сможете, если хотите достичь заявленных целей в статье.
Но данная оптимизация в принципе редко применима на практике, это правда. В прикладных программах, очевидно, так никто делать не станет: код становится слишком сложнее. Цена за выигрыш слишком высока, всем плевать на лишние 300кб.
Так ошибки не будет в данном коде никогда. Вставка
assert_unchecked- это декларация инварианта для структурыS. СтруктуруSмы можем создать только одним способом - через вызовS::new, который проверяет на корректность входные данные.Как я понял, к нему в issue понабежали "советчики" как правильно готовить гит, а
dotexe1337это вообще не интересно - он просто пилит vxkex для себя, не позиционируя себя в качестве "professional programmer". Он гитхаб использовал только в качестве системы тикетов об ошибках и облачного хранения кода (а-ля дропбокс). Я думаю, это и является единственной причиной почему проект был заархивирован на гитхабе.Дополнительные причины почему случилась такая неразбериха с кучей форков и разными коммитами описаны в третьем абзаце:
https://github.com/dotexe1337/VxKex/issues/1#issuecomment-2798345435
Это в вашем моде так? В ваниле это завит от слота: строит тот игрок, который находится выше.
А в турнирном патче, например, сделали псевдорандом.
Да, это известный баг. Занятно, что есть простой способ это обойти. После сохранения надо тут же ещё раз сохранить игру в тот же самый файл. После этого простого действия, при загрузке ход пропускаться не будет.
edition2024 доступен только начиная с 1.85, а вы пытаетесь собрать в 1.83 - естественно он будет ругаться, т.к. не знает как с этим работать.
Какие?
Потому что правила автовывода лайтаймов (lifetime elision) строго определены, и данный случай под эти правила не подходит.
Вот пример кода, где выставление одинакового лайфтайма (времени жизни, ВЖ) во всех местах в функции
foo_incorrectне пройдет проверку БЧ (borrow checker):Надо понимать, что БЧ выполняет проверку ВЖ для каждой функции отдельно, не заглядывая в тела других использующихся функций. То есть, в примере выше - неважно что будет написано в теле функций
foo_correct\foo_incorrect- проверка ВЖ для функцииmainот этого никак не изменится: учитываться будут только сигнатуры вызываемых функций.Если бы для каждой функции анализировались тела всех используемых функций, то время компиляции стремилось бы к бесконечности. Но что хуже - такое поведение вносило бы гораздо больше ошибок из-за неправильного вывода ВЖ. Даже стандартные правила вывода ВЖ иногда приводят к неверному результату, приходится вручную указывать (но это редко).
Компилятор не может знать что вы хотите написать, он может только проверить на корректность написанное.
Так вы в примере не используете
fooповторно вне замыкания. Речь именно об этом.А зачем тогда тащить C++, если в итоге будет C с классами? Так оно и так плюс-минус присутствует в коде ядра, местами. Если я ничего не путаю.
UPD: Ради контейнеров умных ссылок - ну не знаю, как будто бы и не стоит овчинка выделки.
Управление памятью реализовано через владение (ownership). Компилятор на этапе _компиляции_ анализирует код и сам вставляет вызовы free (условно) в то место, где владелец данных (переменная) выходит из скоупа (то есть гарантированно больше никто не может ссылаться на данные).
Контейнеры с подсчётами ссылок есть, но используются не часто.
Примеров, подтверждающих эти возгласы мы конечно же не увидим.
Автору надо ник поменять, а то его тёмное прошлое пестрит даже без резюме. /s
Всю статью не читал, осилил только первый наброс про мутабельность, и вот это правильный комментарий.
Ссылки в расте надо интерпретировать именно так:
&mut T- уникальная ссылка. Из этого свойства как раз и вытекает возможность безопасной мутации объекта.&T- шаренная ссылка. На этот объект может иметь доступ много кто, поэтому в общем случае изменять данные под ней нельзя. Но можно переложить ответственность за контроль корректного доступа (exactly one writer XOR multiple readers) в рантайм с помощьюMutex,RwLockили иных способов Interior Mutability.К сожалению уже устоявшаяся классическая дискриминация по признаку наличия\отсутствия смартфона. Типа, если нету у тебя смартфона, то ты и не человек вовсе.