Блог*
#prog #cpp #моё В C++ есть такая вещь, как strict aliasing. Если коротко, то это предположение компилятора о том, что доступы по указателям (и ссылкам) существенно разных типов не пересекаются между собой. Подробнее про это можно прочитать, например, тут…
В комментариях сказали, что не хватает демонстрации того, что Rust лучше. Что ж, исправляю.
Вот как выглядит идиоматичная функция с аналогичным функционалом (с поправкой на то, что, как и в коде на C++ в постах выше, идиоматично было бы принимать
(также можно было бы переписать эту функцию через доступы по индексам вместо прямого
Возможно ли повторить на Rust семантику из C++? Да... С сигнатурой вида
Вот как выглядит идиоматичная функция с аналогичным функционалом (с поправкой на то, что, как и в коде на C++ в постах выше, идиоматично было бы принимать
value вообще по значению):fn increment(arr: &mut [i32], value: &i32) {
for x in arr {
*x += *value;
}
}
Rust 1.65.0 с флагом -C opt-level=1 выдаёт следующий код: test rsi, rsi
sete al
je .LBB3_4
lea rsi, [rdi + 4*rsi]
mov ecx, dword ptr [rdx]
.LBB3_2:
not al
movzx eax, al
add dword ptr [rdi], ecx
and eax, 1
lea rdi, [rdi + 4*rax]
cmp rdi, rsi
sete al
je .LBB3_4
test rdi, rdi
jne .LBB3_2
.LBB3_4:
ret
Вывод несколько шумный (эти манипуляции с al и eax явно излишни), но самое главное: доступ к содержимому value осуществляется через mov ecx, dword ptr [rdx]
— инструкцию вне цикла! И это вполне ожидаемо: в Rust есть более тонкие способы отслеживать aliasing, чем просто через типы. Тот факт, что набор чисел передаётся через ссылку &mut (и, в отличие от span в C++, это действительно именно ссылка, а не value-тип с ссылочной семантикой), уже позволяет считать все остальные доступы к памяти, как непересекающиеся. поскольку в Rust &mut — это уникальная ссылка. При кодогенерации компилятор может рассчитывать на этот факт, а при создании ссылок проверяет, что ссылка действительно уникальна.(также можно было бы переписать эту функцию через доступы по индексам вместо прямого
for x in arr, но, хотя тело цикла в этом случае чище, компилятор частично раскручивает цикл, что генерировало несколько больше ассемблерного кода, чем имеет смысл помещать в пост)Возможно ли повторить на Rust семантику из C++? Да... С сигнатурой вида
fn increment(arr: *mut [UnsafeCell<MaybeUninit<i32>>], value: *mut MaybeUninit<i32>)
...Ладно, ладно, если что-то не столь страшное, а всё-таки более идиоматичное, но с возможностью пересечения доступа:fn increment(arr: &[Cell<i32>], value: &Cell<i32>) {
for x in arr {
x.set(x.get() + value.get());
}
}
И действительно, если скомпилировать этот код с теми же флагами, то получится вот это: test rsi, rsi
sete cl
je .LBB4_4
lea rax, [rdi + 4*rsi]
.LBB4_2:
not cl
movzx ecx, cl
mov esi, dword ptr [rdx]
add dword ptr [rdi], esi
and ecx, 1
lea rdi, [rdi + 4*rcx]
cmp rdi, rax
sete cl
je .LBB4_4
test rdi, rdi
jne .LBB4_2
.LBB4_4:
ret
Снова кучка лишних операций, но главное — это вот эти две операции в теле цикла: mov esi, dword ptr [rdx]
add dword ptr [rdi], esi
То есть значение читается по ссылке каждый раз, как мы и (не) хотели.👍12🔥1
Блог*
#prog #cpp #моё В C++ есть такая вещь, как strict aliasing. Если коротко, то это предположение компилятора о том, что доступы по указателям (и ссылкам) существенно разных типов не пересекаются между собой. Подробнее про это можно прочитать, например, тут…
Кстати, тот факт, что мы принимаем
Ну а Cell::as_slice_of_cells имеет очень простое оправдание: если мы имеем де-факто мутабельный доступ к какому-то значению, то мы можем разделить его на мутабельные доступы к его отдельным компонентам. При этом как-то подменить слайс целиком, чтобы поменять его длину, и инвалидировать ссылки на его элементы нельзя: все соответствующие методы или требуют уникальную ссылку, или работают только с
&[Cell<i32>], вовсе не означает, что нам нужно бегать по массивам и оборачивать элементы в Cell-ы. Можно воспользоваться двумя методами Cell:fn into_cell_slice<T>(arr: &mut [T]) -> &[Cell<T>] {
Cell::from_mut(arr).as_slice_of_cells()
}
Cell::from_mut работает за счёт того, что забирает переданную mut-ссылку и потому отбирает уникальный доступ до тех пор, пока возвращённая ссылка и её копии не будут дропнуты. Не смотря на то, что &Cell<T> даёт разделяемый доступ с возможностью поменять значение, это не создаёт проблем за счёт того, что изначально переданный доступ заведомо уникален, а доступ по &Cell<T> хоть и и не уникален, но заведомо находится в пределах одного потока: Cell<T> не реализует Sync, или, иными словами, ссылка на Cell не реализует Send, поэтому ссылку на Cell нельзя передать в другой поток. В рамках же одного потока доступы к одной локации в памяти никогда не происходят одновременно, и потому возможность создания гонок данных исключена.Ну а Cell::as_slice_of_cells имеет очень простое оправдание: если мы имеем де-факто мутабельный доступ к какому-то значению, то мы можем разделить его на мутабельные доступы к его отдельным компонентам. При этом как-то подменить слайс целиком, чтобы поменять его длину, и инвалидировать ссылки на его элементы нельзя: все соответствующие методы или требуют уникальную ссылку, или работают только с
Sized типами, которым слайс не является.doc.rust-lang.org
Cell in std::cell - Rust
A mutable memory location.
❤3👍3🔥2
Forwarded from 🇺🇦 Go for two :)
Блог*
#prog #article Falsehoods programmers believe about undefined behavior
#prog #article
Undefined behavior can result in time travel (among other things, but time travel is the funkiest)
Undefined behavior can result in time travel (among other things, but time travel is the funkiest)
Microsoft News
Undefined behavior can result in time travel (among other things, but time travel is the funkiest)
The C and C++ languages are notorious for the very large section of the map labeled here be dragons, or more formally, undefined behavior. When undefined behavior is invoked, anything is possible. For example, a variable can be both true and false.
❤1👍1
В итоге я сегодня на RustCon буду, но без доклада, в отличие от прошлого раза.
Telegram
Блог*
Ребята, всем привет!
В эти непростые времена мы решили поддержать сообщество rust-разработчиков и выложить видеозаписи всех докладов с RustCon 2021 раньше, чем обещали. Смотрите их тут, делитесь с друзьями.
Любим вас, до встречи на наших конференциях ❤️
В эти непростые времена мы решили поддержать сообщество rust-разработчиков и выложить видеозаписи всех докладов с RustCon 2021 раньше, чем обещали. Смотрите их тут, делитесь с друзьями.
Любим вас, до встречи на наших конференциях ❤️
🌚6👍1
Блог*
#prog #rust #rustasync #rustlib #article Как известно, в настоящий момент трейты в Rust не могут иметь асинхронные методы. Причины для этого описаны в статье Никитоса Niko Matsakis why async fn in traits are hard, и если коротко, они связаны с кучей нерешённых…
@goldsteinq про name-it рассказывает. Интересно. Говорит, miri ругается на UB в крейте futures
😱2
Блог*
@goldsteinq про name-it рассказывает. Интересно. Говорит, miri ругается на UB в крейте futures
#prog #rust #rustlib
Узнал про elain.
The type Align<N> is a zero-sized-type with alignment equal to N:
Valid alignments are powers of two less-than-or-equal to 2^28. Supplying an invalid alignment to Align is a type error:
Узнал про elain.
The type Align<N> is a zero-sized-type with alignment equal to N:
use elain::Align;
use core::mem::{align_of, align_of_val};
assert_eq!(align_of::<Align<1>>(), 1);
assert_eq!(align_of::<Align<2>>(), 2);
assert_eq!(align_of::<Align<4>>(), 4);
const FOO_ALIGN: usize = 8;
#[repr(C)]
struct Foo {
_align: Align<FOO_ALIGN>,
}
let foo: Foo = Foo { _align: Align::NEW };
assert_eq!(align_of_val(&foo), 8);Valid alignments are powers of two less-than-or-equal to 2^28. Supplying an invalid alignment to Align is a type error:
use elain::Align;
struct Foo(Align<3>); // Compile Errordocs.rs
elain - Rust
Set the minimum alignments of types using const generics, rather than `#[repr(align(N))]`.
👍7🤔2
Forwarded from Backtracking (Дима Веснин)
пропустил в прошлом году и только сейчас узнал, что в MTG есть земли, целиком состоящие из текста, and i think that's beautiful
👍2🤔2❤1