LINUX.ORG.RU

Rust 1.88.0

 

Rust 1.88.0

0

4

Опубликован следующий стабильный релиз компилятора и стандарта языка Rust.

В этой версии добавлены следующие возможности:

  1. Синтаксис для описания функций без пролога и эпилога. Тело таких функций обязано состоять из naked_asm! блока :
#[unsafe(naked)]
pub unsafe extern "sysv64" fn wrapping_add(a: u64, b: u64) -> u64 {
    // Equivalent to `a.wrapping_add(b)`.
    core::arch::naked_asm!(
        "lea rax, [rdi + rsi]",
        "ret"
    );
}
  1. Возможность объявлять две и более переменных в условных выражениях if / while:
if let Channel::Stable(v) = release_info()
    && let Semver { major, minor, .. } = v
    && major == 1
    && minor == 88
{
    println!("`let_chains` was stabilized in this version");
}
  1. В DSL для условной компиляции cfg добавлены константы true и false, которые так же стали доступны в макросе cfg!.

  2. Добавлено предупреждение о попытке обращения к нулевому указателю для функций, чьи инварианты корректной работы требуют не нулевых адресов. К примеру попытка сконструировать объект std::slice по нулевому адресу:

// Undefined behavior
unsafe { std::slice::from_raw_parts(ptr::null(), 1); }

приведёт к выдаче сообщения. По умолчанию это предупреждение имеет уровень deny-by-default, то есть будет рассмотрено компилятором как ошибка.

Все нововведения перечислены в списке изменений.

>>> Announcing Rust 1.88.0

★★★★★

Проверено: dataman ()
Последнее исправление: dataman (всего исправлений: 3)
Ответ на: комментарий от quantum-troll

Дегенераты, однако, продолжают использовать С (ничто другое они осилить не могут).

Это в линуксе, где gcc есть чуть реже, чем всегда.

Под виндовз оказывается, что дегенератам с зигом удобнее. А как он винапишные функции дергает, так это просто песня.

sarumeister
()
Ответ на: комментарий от quantum-troll

Дегенераты, однако, продолжают использовать С

Ладно, записывайте меня в дегенераты, целевая платформа поддерживает только Си :)

I-Love-Microsoft ★★★★★
()
Ответ на: комментарий от Somebody

Нек-романтичный позитивный, воодушевляющий и непринужденный вайб-кодинг.

sarumeister
()
Ответ на: комментарий от quantum-troll

Ну так то все борщехлебные язычки, и отдельно Rust, как выражается царь - языки-воры, то есть сидят на достижениях программистов C/C++, сами не могут осилить даже компиляторы. Еще есть случаи как у OCaml, у него есть свой компилятор, но он настолько отсталый что это опять доказывает немощность скриптух без богоподобной сишки.

Неможность написать базовые вещи для своего языка это очень показательно. Так что по хорошему, фанаты скриптух должны кланяться C/C++ программистам, и восхвалять их, что допустили возможность использовать их наработки во благо скриптух.

MOPKOBKA ★★★★★
()
Последнее исправление: MOPKOBKA (всего исправлений: 5)
Ответ на: комментарий от Ygor

В языках с семантикой Модулы-2 операнд в выражении if всегда должен иметь тип boolean, это исключает неоднозначности в прочтении.

LongLiveUbuntu ★★★★★
()
Ответ на: комментарий от I-Love-Microsoft

Соборлезную. У нас в «ЗИП - Научприбор» писали на Паскале под микроконтроллеры

LongLiveUbuntu ★★★★★
()
Ответ на: комментарий от LongLiveUbuntu

На языки без паттерн матчинга и структурного связывания смотреть не хочется в современном мире. Эта малая деталь обеспечивает большие возможности для выразительности, без них язык это COBOL (для меня) застрявший в повторении бессмысленных императивных команд.

Нет никакого удовольствия в ручной распаковке значений, или написания более длинных проверок чем они должны быть. А let PATTERN = VALUE лишь обобщение, которое не вносит проблем, но зато очень умно и органично вписывается в язык, для меня это выглядит как очень продуманная деталь.

MOPKOBKA ★★★★★
()
Последнее исправление: MOPKOBKA (всего исправлений: 3)
Ответ на: комментарий от MOPKOBKA

Ну да. Тем более, что .«равно» - это «=», а «присвоить» - ":=". Ошибиться невозможно.

LongLiveUbuntu ★★★★★
()
Ответ на: комментарий от LongLiveUbuntu

В языках с семантикой Модулы-2 операнд в выражении if всегда должен иметь тип boolean, это исключает неоднозначности в прочтении.

Не важно, в этом данном конкретном примере за низкоуровневой абстракцией if скрывается высокоуровневая абстракция match. И подобные выверты они в расте на каждом шагу, вроде как за присваиванием = по дефолту (а не по переопределёной функции) скрывается std:move.

Я как то смотрел уязвимость в ядре линукс на раста: блок кода выглядел как нагромождение разных скобочек в котором просто забыли ещё один блок разных скобочек.

Ygor ★★★★★
()
Ответ на: комментарий от Ygor

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

Сейчас попробую сформулировать мысль, тут будет небольшой подход.

Изначально в высшем мире абстракций программа это алгоритм. Это некое семантическое множество. Семантика, это те действия, которые мы делаем над данными, выраженные в абстрактном виде.

Синтаксис это уже то, что потребляет компилятор. Это низшее звено, по сути последовательности ASCII символов, минус незначащие проблемы, комментарии и тд. Т.е. последовательность лексем.

Чем ниже уровнем язык, тем больше лексем нужно, чтобы выразить ту же семантику. На языке ассемблера может потребоваться три строки по три лексемы для того, чтобы прибавить к одному числу второе. На любом высокоуровневом языке это будет одна строка вида a += b.

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

И синтаксический сахар тут напрямую причастен, т.к. вся его суть в том, чтобы засунуть больше семантики в меньший объём лексем.

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

Но другая крайность также опасна. Когда за одним деревом скрывается целый лес, простите за такую странную аналогию, это может и даёт небольшой выброс дофамина тому, кто написал такой код, который в пару строк делает кучу всего. Но вот тот, кто этот код будет читать, проводить ревью, ему ведь всё равно надо эту семантику восстановить.

Когда отношение числа лексем к набору семантики разумно велико, мозг человека автоматом может разбивать низлежащий алгоритм на части и обрабатывать эти части по отдельности. Когда же число лексем слишком мало, мозг вынужден обрабатывать эти лексемы как единое целое. А объём «мозгового кеша» не у всех людей велик. У меня, например, мал. И попытка понять всю многогранность семантики, заключённую в этих двух строках, может выйти неудачной. Какие-то грани семантики ускользнут. А в этих гранях может и скрывался баг. Как, например, в упомянутому баге - может там так оно и было? И если все эти скобки раскрыть и расставить в несколько разных statement-ов, независимых друг от друга, может код стал бы понятней и шанс того, что ревьюер увидел бы баг, повысился?

С одной стороны синтаксический сахар в уместных местах полезен. С другой стороны его наличие не означает, что его применять нужно. Совсем наоборот. Лично мой подход - избегать синтаксического сахара просто потому, что он есть. И лишь когда код мне кажется излишне многословным, я могу сделать какие-то тривиальные трансформации, уменьшив число лексем.

vbr ★★★★★
()
Последнее исправление: vbr (всего исправлений: 1)
Ответ на: комментарий от vbr

Т.е. к примеру код выше можно переписать как

if let Channel::Stable(v) = release_info() {
    if let Semver { major, minor, .. } = v {
        if major == 1 && minor == 88 {
            println!("`let_chains` was stabilized in this version");
        }
    }
}

А может быть (если это применимо в данном случае) и как

let Channel::Stable(v) = release_info() else {
    return;
};
let Semver { major, minor, .. } = v else {
    return;
}
if major == 1 && minor == 88 {
    println!("`let_chains` was stabilized in this version");
}

Да, наверное именно данный пример проще в исходной формулировке, но чуть его усложни и уже не факт. И последний вариант лично мне читать проще, например.

vbr ★★★★★
()
Ответ на: комментарий от vbr

Есть например в C# похожий синтаксис на switch:

currentBalance += transaction switch
{
    (TransactionType.Deposit, var amount) => amount,
    (TransactionType.Withdrawal, var amount) => -amount,
    _ => 0.0, 
};

Читается в разы лучше.

Ygor ★★★★★
()
Ответ на: комментарий от MOPKOBKA

У раста есть Cranelift, так что речь может идти об отсталости, а не об отсутствии.

Так что по хорошему, фанаты скриптух должны кланяться C/C++ программистам, и восхвалять их, что допустили возможность использовать их наработки во благо скриптух.

Не, только С++ программистам. И я готов признать превосходство современного С++ над растом.

Раст это такой учебный язык, которые люди учат перед тем, как начать учить современный С++ или средства доказательства теорем (по вкусу). Но к сожалению, подавляющее большинство программистов не способно осилить даже такой учебный язык.

quantum-troll ★★★★★
()
Ответ на: комментарий от Ygor

Они, конечно, придумали максимально нечитаемый синтаксис. В гипотетической Java это выглядело бы как-то так:

currentBalance +=
  switch (transaction) {
    case TransactionType.Deposit(amount) -> amount;
    case TransactionType.Withdrawal(amount) => -amount;
    default -> 0.0;
  };

Тут по крайней мере понятно, что именно сопоставляется шаблонам. А в исходном примере я думал, что сопоставляется результат выражения (currentBalance += transaction).

vbr ★★★★★
()
Последнее исправление: vbr (всего исправлений: 2)
Ответ на: комментарий от vbr

А объём «мозгового кеша» не у всех людей велик. У меня, например, мал.

Если бы только у тебя…У подавляющего большинства людей емкость рабочей и временной памяти это буквально несколько объектов. Она еще и работает чрезвычайно медленно. Именно по этой причине сложные конструкции или функции где больше 5-6 параметров крайне нежелательны. И программы нужно писать в первую очередь так чтобы их мог прочесть человек и уже вторую чтобы их мог выполнить компьютер. Насчет сравнительной сложности средств разработки не согласен. Пропасть между ассемблером и С гораздо больше чем между С и любым другим высокоуровневым языком. Не только писанина, это еще и множество решений нужно принять - какие регистры использовать, куда помещать промежуточные результаты, адресация, циклы, примитивные условные операторы.

FreeLiver ★★★
()
Ответ на: комментарий от Ygor

за низкоуровневой абстракцией if скрывается высокоуровневая абстракция match.

одинаковый у них уровень абстракции, вообщем-то if это частный случай match

скрывается std:move

никто ничего ни скрывают, с самого начала говорят - по умолчанию всё мувается, если не имплементирован copy трейт. Ну и уродливее чем в плюсах мув семантики нет нигде.

zurg
()
Ответ на: комментарий от quantum-troll

Не, только С++ программистам.

С++ стоит на С, как будет запускаться С++ компилятор если не будет ядра, утилит, сишных библиотек которые использует gcc, и которые так распространены в GNU/Linux мире?

У раста есть Cranelift, так что речь может идти об отсталости, а не об отсутствии.

Просто накинуть хоть каких то нативных команд для «компиляции» это простейшая задача.

MOPKOBKA ★★★★★
()
Последнее исправление: MOPKOBKA (всего исправлений: 1)
Ответ на: комментарий от zurg

за низкоуровневой абстракцией if скрывается высокоуровневая абстракция match.

одинаковый у них уровень абстракции, вообщем-то if это частный случай match

Если взять определение из википедии, то match явно более высокоуровневая абстракция.

Только жалоба Ygor мне кажется необоснованной, это zero-cost абстракция. Сам С многое вносит, пусть Ygor подумает как компилируется деление uint64_t на 32 битных платформах. Еще С не только скрывает сложные операции, но и не дает доступа к низкоуровневым, вот как в «переносимом ассемблере» записать без нестандартных расширений этот простейший код?

int64_t mul(int64_t a, int64_t b) 
{
    return ((__int128)a * b) >> 32;
}

mul:
mov rax, rdi
imul rsi
shrd rax, rdx, 32
ret

MOPKOBKA ★★★★★
()
Последнее исправление: MOPKOBKA (всего исправлений: 1)

Возможность объявлять две и более переменных в условных выражениях if / while:

логический and что-ли? А раньше они как писали? лесенкой if-else?

Kolins ★★★★★
()
Ответ на: комментарий от MOPKOBKA

С++ стоит на С, как будет запускаться С++ компилятор если не будет ядра, утилит, сишных библиотек которые использует gcc, и которые так распространены в GNU/Linux мире?

Ну в GNU/Linux много легаси. В винде ядро написано вроде как на плюсах, в Haiku тоже всё написано на плюсах, так что жить без сишки вполне можно.

Просто накинуть хоть каких то нативных команд для «компиляции» это простейшая задача.

Но Cranelift делает не это.

quantum-troll ★★★★★
()
Ответ на: комментарий от quantum-troll

Ну в GNU/Linux много легаси. В винде ядро написано вроде как на плюсах, в Haiku тоже всё написано на плюсах, так что жить без сишки вполне можно.

Если открыть утекшие исходники NT Kernel, то там чистый С. У Haiku игрушечное ядро, игрушечные ОС на чем только не пишут.

MOPKOBKA ★★★★★
()
Ответ на: комментарий от Kolins

Суть в том что между && можно определить новые переменные.

MOPKOBKA ★★★★★
()
Ответ на: комментарий от FreeLiver

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

Не согласен.

Не только писанина, это еще и множество решений нужно принять - какие регистры использовать, куда помещать промежуточные результаты, адресация, циклы, примитивные условные операторы.

Давай разберем.

какие регистры использовать

Какие регистры использовать для аргументов определяет соглашение о вызовах, можно просто его использовать. Внутри функции переменные можно размещать в стеке, это позволит вызывать их рекурсивно.

куда помещать промежуточные результаты

В переменные?

адресация

Что адресация? Под DOS актуально конечно, но сейчас что о ней думать?

циклы

do {
  f();
} while (--i);
do:
  call f
  dec [i]
  jnz do
MOPKOBKA ★★★★★
()
Последнее исправление: MOPKOBKA (всего исправлений: 2)
Ответ на: комментарий от quantum-troll

У Windows же не монолитное ядро, драйверы отдельно. Ну и драйверы не отменяют наличие С все же, заметь.

MOPKOBKA ★★★★★
()
Последнее исправление: MOPKOBKA (всего исправлений: 1)
Ответ на: комментарий от vbr

Сложный шаблон раздует на С коде в десять или более раз, вот там реально леса за деревьями не увидишь. К ADT/match еще GADT прилагается в хорошем языке, там дополнительная защита от ошибок за счет крайне простой идеи.

MOPKOBKA ★★★★★
()
Последнее исправление: MOPKOBKA (всего исправлений: 2)
Ответ на: комментарий от MOPKOBKA

Внутри функции переменные можно размещать в стеке, это позволит вызывать их рекурсивно.

Это медленно. Кроме того, ты забыл, что в высокоуровневых языках значения не ограничиваются машинными словами.

Так что `a = b` это в общем случае далеко не один `mov`. Но с другой стороны, хотелось бы вообще избежать лишних мувов, если есть такая возможность.

quantum-troll ★★★★★
()
Ответ на: комментарий от quantum-troll

Это медленно.

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

Так что `a = b` это в общем случае далеко не один `mov`.

Ну в случае С, это может быть memcpy структур, вот как оно делается, можно написать макрос что бы указывать всего одну команду и размер:

mov rdi, %dst%
mov rsi, %src%
mov rcx, %sizeof%
rep movsb
struc point_t {
  .x dd ?
  .y dd ?
}

macro MEMCPY DST, SRC, SIZEOF {
  mov rdi, DST
  mov rsi, SRC
  mov rcx, SIZEOF
  rep movsb  
}

MEMCPY a, b, sizeof.point_t  

MOPKOBKA ★★★★★
()
Последнее исправление: MOPKOBKA (всего исправлений: 1)
Ответ на: комментарий от MOPKOBKA

jnz do

А если нужно составное условие чуть посложнее jnz?

Насчет стека для хранения переменных и соглашения о вызовах не понял. Если это не встроенный асм, могу как мне удобно передавать. Но вот кстати ещё одна проблема асма - громозкий вызов внешних библиотек. Хранить переменные на стеке можно, но неэффективно, зачем тогда нужен асм, gcc с ключем -O2 их эффективнее по регистрам раскидает. А вообщем я на асме уже лет 30 не писал, не готов дальше спорить, всё равно интеллектом задавишь :)

FreeLiver ★★★
()
Ответ на: комментарий от FreeLiver

А если нужно составное условие чуть посложнее jnz?

Есть and, or в распоряжении программиста. Но конечно одна строка в С может выйти в несколько команд на ассемблере.

Мы тут паттерн матчинг обсуждаем, одна строка паттерн матчинга вполне в десяток строк на С выйдет тоже.

Насчет стека для хранения переменных и соглашения о вызовах не понял. Если это не встроенный асм, могу как мне удобно передавать.

Можешь, я не отрицаю. Но даже если нету взаимодействия с С, обычно следуют какому то соглашению, например что бы были регистры которые функции не трогают. И в такое соглашение можно посмотреть, и распределять регистры просто от первого до последнего по номеру.

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

Но вот кстати ещё одна проблема асма - громозкий вызов внешних библиотек.

Для этого макрос пишут, вот пример из FASM\EXAMPLES\DIALOG.ASM

invoke CheckRadioButton, [hwnddlg], ID_ICONERROR, ID_ICONWARNING, ID_ICONINFORMATION

Хранить переменные на стеке можно, но неэффективно, зачем тогда нужен асм, gcc с ключем -O2 их эффективнее по регистрам раскидает.

Ну вот можно взять лямбды в С, правильно написать их, передать все контексты и уследить за памятью, это задача посложнее чем провести распределение регистров в уме. Это покажет пропасть между С и OCaml, равную пропасти между FASM и C?

MOPKOBKA ★★★★★
()
Последнее исправление: MOPKOBKA (всего исправлений: 6)
Ответ на: комментарий от FreeLiver

Если представить ADT по наиболее краткой модели для C/C++ в виде struct { enum; union; }, то вот такое условие с одним OR

match ast_node with
| Ast.AddOp (Ast.Num x, Ast.IncOp (Ast.Var name))
| Ast.AddOp (Ast.IncOp (Ast.Var name), Ast.Num x) when x > 10 -> 
  ... code ...
Наивно можно развернуть вот в это:
bool if_true = false;
std::string name;
int x;

if (ast_node.kind == Ast::ADD_OP) {
  if (ast_node.add_op.left.kind == Ast::NUM) {
    if (ast_node.add_op.right.kind == Ast::INC_OP) {
      if (ast_node.add_op.right.inc_op.value.kind == Ast::VAR) {
        x = ast_node.add_op.left.num.value;
        name = ast_node.add_op.right.inc_op.value.var.name;
        if_true = true; 
      }
    }
  }
}

if (ast_node.kind == Ast::ADD_OP) {
  if (ast_node.add_op.right.kind == Ast::NUM) {
    if (ast_node.add_op.left.kind == Ast::INC_OP) {
      if (ast_node.add_op.left.inc_op.value.kind == Ast::VAR) {
        x = ast_node.add_op.right.num.value;
        name = ast_node.add_op.left.inc_op.value.var.name;
        if_true = true; 
      }
    }
  }
}

if (if_true && x > 10) {
 ... code ...
}
Может можно проще развернуть, но это уже дополнительные мыслительные процессы. Какой то более сложный матчинг разворачивать мне лень для демонстрации, но уже в этом примере можно понять что это не будет развесисто.

MOPKOBKA ★★★★★
()
Последнее исправление: MOPKOBKA (всего исправлений: 2)
Ответ на: комментарий от MOPKOBKA

Это покажет пропасть между С и OCaml, равную пропасти между FASM и C?

Сожалею, но я ничего не знаю про OCaml, знаю про более приземленные вещи вроде Python, java, но что-то ведь мы еще забыли сравнивая C и макроассемблер, а именно арифметические выражения (и логические тоже), да я знаю всё можно расписать командами CPU и FPU (и sse, avx…), но выразительность и лаконичность будут потеряны вместе с переносимостью кстати.

FreeLiver ★★★
()
Ответ на: комментарий от FreeLiver

Да, арифметика трудно выражается, но если начать обрабатывать векторы, матрицы, длинные числа, то разницы между ассемблером и С почти не будет, и там и там будут вызовы функций, и никаких перегруженных операторов. А вот оставаясь в пределах базовых типов (int, float) конечно С победит.

В С++ можно перегрузить операторы, и работать через привычные операторы. А в С даже может быть проблема не только с тем что нужно функции постоянно вызывать для любого сложения, но и с тем что нужно будет на каждый результат вызывать очистку памяти.

Хотя если говорить о препроцессоре ... в fasm он может решить линейное уравнение, а в С он довольно слабый.

MOPKOBKA ★★★★★
()
Последнее исправление: MOPKOBKA (всего исправлений: 3)
Ответ на: комментарий от MOPKOBKA

Если взять определение из википедии, то match явно более высокоуровневая абстракция.

Это паттерн матчинг более высокоуровневая абстракция. Но даже и в сишечке if не ниже уровнем switch-а, и наоборот.

Еще С не только скрывает сложные операции, но и не дает доступа к низкоуровневым, вот как в «переносимом ассемблере» записать без нестандартных расширений этот простейший код?

Ну так очевидно же, надо брать современный полноценный системный язык:

fn mul(a:i64, b:i64) -> i64 {
    ((a as i128 * b as i128)>>32) as i64
}
mul:
        mov     rax, rsi
        imul    rdi
        shrd    rax, rdx, 32
        ret

и быстро, и без УБ. Ты бы ещё бейсик притаранил, ну, тот которой ровесник сишечки, во всех смыслах.

zurg
()
Ответ на: комментарий от zurg

Это паттерн матчинг более высокоуровневая абстракция.

Я же так и написал.

Но даже и в сишечке if не ниже уровнем switch-а, и наоборот.

Сейчас разница между ними стерта, но раньше switch предназначался для генерации таблиц переходов, что делает его явно более продвинутым инструментом.

Ну так очевидно же, надо брать современный полноценный системный язык

Ты кажется сути примера не понял, а теперь покажи mul для (i128, i128) -> i128. Так то если взять самый новый стандарт С, его можно написать:

typedef _BitInt(128) int128_t;
typedef _BitInt(256) int256_t;

int128_t mul(int128_t x, int128_t y) 
{
    return ((int256_t)x * y) >> 64;
}

MOPKOBKA ★★★★★
()
Последнее исправление: MOPKOBKA (всего исправлений: 2)
Ответ на: комментарий от zurg

современный полноценный системный язык

это ведь не про руст, правда?

a as i128 * b as i128

вот тут явно написано, что множители 128 битные. значит произведение должно быть 256 битным. это и имелось ввиду?

alysnix ★★★
()
Ответ на: комментарий от alysnix

это ведь не про руст, правда?

А какие-то другие есть? плюсы, ада и какие-нибудь обероны не современные, ну и плюсцы только условно системный(слишком error prone), какие-нибудь Zig-и слишком сырые или маргинальные, а сишечка все три не, прямо бинго по убогости. Так вот и получается, если объективно, кроме раста выбрать нечего.

значит произведение должно быть 256 битным.

Нет, гарантированно влезает в 128 так как операнды гарантированно 64 бит. А вот с шифтом только на 32 может быть переполнение знакового, и в расте это точно не УБ. А в си УБ

zurg
()
Последнее исправление: zurg (всего исправлений: 1)
Ответ на: комментарий от zurg

Хотя про УБ наврал, тут только каст от большего знакового к меньшему и это, вроде, только implementation defined. И вот такого рода гадания сами по себе уже нихеровая проблема С и её прямого потомка.

zurg
()
Последнее исправление: zurg (всего исправлений: 1)
Ответ на: комментарий от zurg

не, ну по хорошему произведение должно автоматом расширять в два раза битность, и только потом приводится к требуемой битности, по требованию заказчика. как происходит в процессоре.

а такая декларация неверна. она не safe и требует нетривиальных предсловий (чтобы реальное произведение влезло в нужную битность)

fn mul(a:i64, b:i64) -> i64 

такие верны

fn mul(a:i32, b:i32) -> i64 
fn mul(a:i64, b:i64) -> i128 
alysnix ★★★
()
Ответ на: комментарий от alysnix

Мой uint64 mul(uint64, uint64) реализует умножение fixed point с частями 32:32, который использует аппаратные возможности для умножения, и обходится без эмуляции 128 битных чисел. Если нужна какая то скорость, то возврат i128 как раз будет некорректным.

Умножение почти везде дает результат в два регистра, что дает бесплатное умножение i128 если есть умножение i64.

MOPKOBKA ★★★★★
()
Последнее исправление: MOPKOBKA (всего исправлений: 1)
Ответ на: комментарий от MOPKOBKA

Если нужна какая то скорость, то возврат i128 как раз будет некорректным.

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

вообще говоря операции должны быть (набор кулхацкера) (если модель 32 битная)

i64 mul(i32, i32) //просто умножает c расширением
i32 mul_restricted(i32, i32)//умножает без гарантий
i32 mul_div(i32, i32, i32) //умножает с расширением и сразу делит
i32 mul_shr(i32, i32, i32) //умножает с расширением и сдвигает вправо
alysnix ★★★
()
Последнее исправление: alysnix (всего исправлений: 1)
Ответ на: комментарий от Ygor

Это match в Rust - базовый оператор для сопоставления с образцом. Аналогичный код на Rust будет таким:

let current_balance += match transaction {
    (TransactionType::Deposit, amount) => amount,
    (TransactionType::Withdrawal, amount) => -amount,
    _ => 0.0, 
};

if let используется, когда нужно разматчить только в один паттерн, потому что match требует полного покрытия всех возможных вариантов, в отличии от.

freecoder
()
Последнее исправление: freecoder (всего исправлений: 1)
Ответ на: комментарий от freecoder

Вообще новые имена переменных в Rust вводятся только при сопоставлении и в составе образца! Это правило просто и универсально в рамках языка. В первой редакции Rust-book даже не было терминологии «ввести переменную», а говорилось о «связывании имён (name binding)», что было корректнее, но обескураживало новичков.

let x = foo();

Здесь происходит сопоставление с образцом, в результате которого значение выражения, которое вернул вызов foo(), сопоставляется с паттерном слева, состоящим только из одного нового имени x, в результате чего это имя связывается со всем значением.

let Point { x, y: b } = foo();

Здесь уже паттерн сложнее, новое имя x связывается со значением одноимённого поля экземпляра структуры Point, который возвращается вызовом foo(). А новое имя b связывается со значением поля y.

Так как это операция let и сопоставление полное, то введённые имена живут до конца текущего скоупа. Если нужно условное сопоставление, где новые имена будут жить только в новом скоупе (блоке), то можно использовать match или if let:

if let Some(val) = bar() {
    println!("Можно использовать имя val = {val}");
}
...
match bar() {
    Some(val) => println!("Можно использовать имя val = {val}"),
    None => println!("Вызов вернул вариант None"),
}

Так что в Rust новые имена «переменных» могут вводиться только в составе образца в операциях: match, let, let ... else, if let, ... else if let, while let, for, в аргументах функций и замыканий. И больше - нигде. Для цикла for это приводит к довольно элегантным конструкциям:

for i in 0..5 {
    println!("Iteration: {i}");
}


for (i, item) in [2, 5, 7].into_iter().enumerate() {
    println!("Iteration: {i}, value: {item}");
}

Запустить: https://play.rust-lang.org/?version=stable&mode=debug&edition=2024&gist=f3f1da4e52def36e39f33103e15e9fa8

freecoder
()
Последнее исправление: freecoder (всего исправлений: 2)
Для того чтобы оставить комментарий войдите или зарегистрируйтесь.