Обновить
1
Fenex@Fenex

Пользователь

1
Подписчики
Отправить сообщение

В примерах статьи передача идёт не по ссылке, а по значению (передача владения) - это и обсуждаем. При передаче по значению используется 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
    }
}

В статье на C указывают код, аналог которого реализуют:

HAL_ADC_Start_DMA(
    &hadc1,
    (uint32_t *)adc_buffer,
    1024
);

buffer, очевидно, предполагается с move-семантикой (то есть оно не реализует трэйт Copy) так как мы хотим получить этот буффер во владение, а при вызове wait() - отдать владение обратно вызывающему.

и задумываешься о качестве того, как вы формулируйте свои промты

Тут ещё неплохо было бы уточнять, с какими LLM имеется опыт. Я уже сталкивался с таким же: человечек упорно стоял на том, что LLM ничего не могут и делают неправильно почти всегда. В конце концов выяснилось, что он использовал какую-то локальную LLM, а не платную версию LLM вроде тех что предоставляют Cursor/Claude/etc :\

"цитата" может быть получена из того же /dev/urandom

Я генерирую пароли этой командой:

base64 < /dev/random | head -c 10

Это стандартный подход в расте: прячем все unsafe-операции за безопасным API, которое будет проверять входные данные и убеждаться в том, что никакие инварианты нарушены не будут. Ничего лучшего вы придумать не сможете, если хотите достичь заявленных целей в статье.

Но данная оптимизация в принципе редко применима на практике, это правда. В прикладных программах, очевидно, так никто делать не станет: код становится слишком сложнее. Цена за выигрыш слишком высока, всем плевать на лишние 300кб.

Автор избавился от panic добавив assert_unchecked, что определённо хуже, т. к. последнее в случае ошибки в коде тихо завалит или попортит программу.

Так ошибки не будет в данном коде никогда. Вставка assert_unchecked - это декларация инварианта для структуры S. Структуру S мы можем создать только одним способом - через вызов S::new, который проверяет на корректность входные данные.

Но и он внезапно 30 сентября слился с Гитхаба, перенеся разработку на свой сайт.

Как я понял, к нему в issue понабежали "советчики" как правильно готовить гит, а dotexe1337 это вообще не интересно - он просто пилит vxkex для себя, не позиционируя себя в качестве "professional programmer". Он гитхаб использовал только в качестве системы тикетов об ошибках и облачного хранения кода (а-ля дропбокс). Я думаю, это и является единственной причиной почему проект был заархивирован на гитхабе.

Дополнительные причины почему случилась такая неразбериха с кучей форков и разными коммитами описаны в третьем абзаце:
https://github.com/dotexe1337/VxKex/issues/1#issuecomment-2798345435

Если в один ход оба игрока заканчивают одно и то же чудо, то построится оно у того, у кого было по итогу вложено больше "производства".

Это в вашем моде так? В ваниле это завит от слота: строит тот игрок, который находится выше.
А в турнирном патче, например, сделали псевдорандом.

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

edition2024 доступен только начиная с 1.85, а вы пытаетесь собрать в 1.83 - естественно он будет ругаться, т.к. не знает как с этим работать.

А если обновить cargo то какие-то другие ошибки сыпет

Какие?

Потому что правила автовывода лайтаймов (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 от этого никак не изменится: учитываться будут только сигнатуры вызываемых функций.

Если бы для каждой функции анализировались тела всех используемых функций, то время компиляции стремилось бы к бесконечности. Но что хуже - такое поведение вносило бы гораздо больше ошибок из-за неправильного вывода ВЖ. Даже стандартные правила вывода ВЖ иногда приводят к неверному результату, приходится вручную указывать (но это редко).

Компилятор не может знать что вы хотите написать, он может только проверить на корректность написанное.

Так вы в примере не используете foo повторно вне замыкания. Речь именно об этом.

А зачем тогда тащить C++, если в итоге будет C с классами? Так оно и так плюс-минус присутствует в коде ядра, местами. Если я ничего не путаю.

UPD: Ради контейнеров умных ссылок - ну не знаю, как будто бы и не стоит овчинка выделки.

Управление памятью реализовано через владение (ownership). Компилятор на этапе _компиляции_ анализирует код и сам вставляет вызовы free (условно) в то место, где владелец данных (переменная) выходит из скоупа (то есть гарантированно больше никто не может ссылаться на данные).

Контейнеры с подсчётами ссылок есть, но используются не часто.

Примеров, подтверждающих эти возгласы мы конечно же не увидим.

Автору надо ник поменять, а то его тёмное прошлое пестрит даже без резюме. /s

Всю статью не читал, осилил только первый наброс про мутабельность, и вот это правильный комментарий.

Ссылки в расте надо интерпретировать именно так:

  • &mut T - уникальная ссылка. Из этого свойства как раз и вытекает возможность безопасной мутации объекта.

  • &T - шаренная ссылка. На этот объект может иметь доступ много кто, поэтому в общем случае изменять данные под ней нельзя. Но можно переложить ответственность за контроль корректного доступа (exactly one writer XOR multiple readers) в рантайм с помощью Mutex, RwLock или иных способов Interior Mutability.

К сожалению уже устоявшаяся классическая дискриминация по признаку наличия\отсутствия смартфона. Типа, если нету у тебя смартфона, то ты и не человек вовсе.

Информация

В рейтинге
4 128-й
Откуда
Новосибирск, Новосибирская обл., Россия
Дата рождения
Зарегистрирован
Активность

Специализация

Бэкенд разработчик, Администратор серверов
Rust
Linux
SQL
MongoDB
C#
JavaScript