Сделаем слайс по частям и превратим:
Оно работает! Но кое-что меня всё же беспокоит: порядок полей в представлении слайсов не зафиксирован и может поменяться в любой версии. Можем ли мы как-то от этого защититься? Вообще, да... Но нам потребуются фичи nightly (да, я вас немного обманул, когда сказал, что nightly не понадобится, но с другой стороны, для кода, собственно создающего строку, он не нужен):
Есть ещё кое-что, что мне не очень нравится: мы используем массив размером 20 байт в надежде на то, что их хватит для форматирования числа. Что будет, если этого размера не хватит? Давайте понизим размер массива до единицы и проверим:
const DECOMPOSED: ([u8; 20], usize) = to_ascii(42);(В том, что мы берём ссылку от константы, нет ничего плохого из-за static promotion)
const STR: &str = unsafe { std::mem::transmute(RawSlice {
ptr: &DECOMPOSED.0 as *const _, // &[u8] неявно приводится к *const [u8],
// а тот уже кастуется в *const u8
len: DECOMPOSED.1,
})};
fn main() {
assert_eq!(STR, "42");
}
Оно работает! Но кое-что меня всё же беспокоит: порядок полей в представлении слайсов не зафиксирован и может поменяться в любой версии. Можем ли мы как-то от этого защититься? Вообще, да... Но нам потребуются фичи nightly (да, я вас немного обманул, когда сказал, что nightly не понадобится, но с другой стороны, для кода, собственно создающего строку, он не нужен):
mod _sanity_check {Здесь мы создаём сырой указатель на слайс (и, насколько я понимаю, в данном случае вызов
use super::RawSlice;
use std::mem::transmute;
const _RAW_SLICE_HAS_RIGHT_REPR: [(); 1] = {
const SENTINEL: usize = 1342;
const SENTINEL_SLICE: RawSlice = RawSlice {
ptr: std::ptr::null(),
len: SENTINEL,
};
[(); (<*const [u8]>::len(unsafe { transmute(SENTINEL_SLICE) }) == SENTINEL) as _]
};
}
transmute
безопасен, поскольку у сырых толстых указателей значительно менее строгие требования к корректности, чем к ссылкам на слайс) с заданной длиной, а потом вынимаем её при помощи метода len сырого указателя. Если мы угадали с порядком полей, то сравнение длин возвратит true
, которое будет скастовано в 1usize
и, таким образом, образует литерал массива нужного типа. В данный момент код компилируется. Давайте поменяем порядок полей в RawSlice
:#[repr(C)]...и посмотрим, что скажет компилятор:
struct RawSlice {
len: usize,
ptr: *const u8,
}
...expected an array with a fixed size of 1 element, found one with 0 elements
Отлично, ровно то, что мы и хотели (на самом деле мы хотели бы более внятное сообщение об ошибке, но пока что имеем, что имеем).Есть ещё кое-что, что мне не очень нравится: мы используем массив размером 20 байт в надежде на то, что их хватит для форматирования числа. Что будет, если этого размера не хватит? Давайте понизим размер массива до единицы и проверим:
error: any use of this value will cause an errorЧто ж, ожидаемо. Но брать размер с запасом всё же не хочется. Мы не можем знать наперёд, сколько цифр понадобится... Хотя, погодите-ка, можем!
--> src/main.rs:27:9
|
27 | ret[i] = (n % 10) as u8 + b'0';
| ^^^^^^
| |
| index out of bounds: the length is 1 but the index is 1
| inside `to_ascii` at src/main.rs:27:9
| inside `DECOMPOSED` at src/main.rs:50:38
...
50 | const DECOMPOSED: ([u8; 1], usize) = to_ascii(42);
| --------------------------------------------------
|
= note: `#[deny(const_err)]` on by default
const fn digits_len(mut n: u32) -> usize {Отлично, это всё более-менее работает. Но это всё ещё ногострельно! Тут довольно много кода, который можно написать неправильно, а собственно число, для которого делается строка, приходится использовать дважды (да, я в курсе, что можно просто с запасом взять, нет, я не хочу так делать). Можем ли мы всё это как-то инкапсулировать? Можем! При помощи макроса:
if n == 0 {
return 1;
}
let mut n_digits = 0;
while n != 0 {
n /= 10;
n_digits += 1;
}
n_digits
}
const LEN: usize = digits_len(42);
const fn to_ascii(mut n: u32) -> ([u8; LEN], usize) {
...
macro_rules! make_literal {Проверим:
($n:expr) => {{
// ^первая пара скобок является частью синтаксиса macro_rules!,
// а вторая открывает блок
const VALUE: u32 = $n;
// далее весь код почти без изменений,
// только с заменой конкретного значения на VALUE
const STR: &str = ...;
STR
}}
}
const STR: &str = make_literal!(41 + 1);Оно работает!
fn main() {
assert_eq!(STR, "42");
}
GitHub
rfcs/text/1414-rvalue_static_promotion.md at master · rust-lang/rfcs
RFCs for changes to Rust. Contribute to rust-lang/rfcs development by creating an account on GitHub.
...С другой стороны, зачем ограничиваться именно
Проверим:
Оно работает! Хотя... Работает ли?
И...
Ясно, нам нужно что-то иное: остаток от деления отрицательного числа на положительное является отрицательным, а нам нужно положительное число, у которого нужно поменять знак. Что, сделаем для этого функцию и запихнём в макрос:
Другие функции нам тоже нужно поменять, чтобы печатать минус:
Протестируем:
Работает!
...Закончили ли мы на этом? Нет! Во-первых, я не хочу указывать каждый раз тип выражения. Во-вторых, из-за ограничений
Проверим:
Вот теперь всё работает. Как всегда, код в гисте.
u32
? Наш код вполне может работать с другими типами! Давайте это исправим:macro_rules! make_literal {
(($n:expr) : $ty:ty) => {{
// то же, что и было, но заменяем u32 на $ty
STR: &str = ...;
STR
}}
}
Проверим:
const STR: &str = make_literal!((41 + 1): u32);
fn main() {
assert_eq!(STR, "42");
}
Оно работает! Хотя... Работает ли?
const STR: &str = make_literal!((-41 - 1): i32);
fn main() {
assert_eq!(STR, "-42");
}
И...
error: any use of this value will cause an error
--> src/main.rs:42:26
|
42 | ret[i] = (n % 10) as u8 + b'0';
| ^^^^^^^^^^^^^^^^^^^^^
| |
| attempt to compute `254_u8 + 48_u8`, which would overflow
| inside `to_ascii` at src/main.rs:42:26
| inside `DECOMPOSED` at src/main.rs:59:48
Ясно, нам нужно что-то иное: остаток от деления отрицательного числа на положительное является отрицательным, а нам нужно положительное число, у которого нужно поменять знак. Что, сделаем для этого функцию и запихнём в макрос:
const fn extract_digit(n: $ty) -> $ty {
let mut ret = n % 10;
#[allow(unused_comparisons)]
// ^ сравнение не имеет смысла для беззнаковых чисел
if ret < 0 {
// мы не можем написать ret = -ret,
// поскольку унарный минус не определён для беззнаковых
ret = 0-ret;
}
ret
}
Другие функции нам тоже нужно поменять, чтобы печатать минус:
const fn digits_len(mut n: $ty) -> usize {
if n == 0 {
return 1;
}
let mut n_digits = 0;
#[allow(unused_comparisons)]
if n < 0 {
n_digits += 1;
}
...
}
...
const fn to_ascii(mut n: $ty) -> ([u8; LEN], usize) {
#[allow(unused_comparisons)]
let is_negative = n < 0;
...
while n != 0 {
ret[i] = extract_digit(n) as u8 + b'0';
n /= 10;
i += 1;
}
if is_negative {
ret[i] = b'-';
i += 1;
}
...
Протестируем:
const STR: &str = make_literal!((-41 - 1): i32);
fn main() {
assert_eq!(STR, "-42");
}
Работает!
...Закончили ли мы на этом? Нет! Во-первых, я не хочу указывать каждый раз тип выражения. Во-вторых, из-за ограничений
macro_rules!
после expr
нельзя ставить двоеточие, из-за чего мне пришлось внести в синтаксис раздражающие скобки. Как известно, всякую проблему можно решить ещё одним слоем абстракции, поэтому я именно этим и воспользуюсь: я сделаю макрос, который принимает имя и тип и генерирует ещё один макрос с переданным именем, который принимает выражение нужного типа и уже возвращает константу:macro_rules! make_literal_maker {
($name:ident : $ty:ty) => {
macro_rules! $name {
($n:expr) => {{
// весь остальной код без изменений
}}
}
}
}
Проверим:
make_literal_maker!(make_str_literal_from_usize: usize);
make_literal_maker!(make_str_literal_from_i8: i8);
const STR_UNSIGNED: &str = make_str_literal_from_usize!(41 + 3 - 2);
const STR_SIGNED: &str = make_str_literal_from_i8!(-100 - 1);
fn main() {
assert_eq!(STR_UNSIGNED, "42");
assert_eq!(STR_SIGNED, "-101");
}
Вот теперь всё работает. Как всегда, код в гисте.
Gist
Реализация макроса по переводу константных числовых значений в строковые константы
Реализация макроса по переводу константных числовых значений в строковые константы - main.rs
От этого поста телеге немного поплохело: когда я перематывал поле ввода наверх, а потом обратно в конец, часть написанного в конце пропадало и было недоступно для редактирования. К счастью, этот баг был чисто визуальным — переключение между каналами вернуло набранное — но, блин, Дуров, какого хрена?
https://t.iss.one/ihatereality/1610
> совершенно беcсмысленный, возведённый в абсолют бред
АХАХАХАХАХАХАХАХА
Это ты ещё compile-time FizzBuzz не видел.
UPD: я его написал.
> совершенно беcсмысленный, возведённый в абсолют бред
АХАХАХАХАХАХАХАХА
Это ты ещё compile-time FizzBuzz не видел.
UPD: я его написал.
Telegram
Мне не нравится реальность
Антон делает то, что я люблю в программировании больше всего — совершенно беcсмысленный, возведённый в абсолют бред, на который потрачено больше времени, чем стоило бы, но зато... зато красиво!
dereference_pointer_there/1210
dereference_pointer_there/1210
У меня для вас две новости касательно Ильи aka @optozorax_dev.
Во-первых, он начинает карьеру фуд-блогера.
Во-вторых, у него сегодня день рождения! Поздравляю, Илья!
Во-первых, он начинает карьеру фуд-блогера.
Во-вторых, у него сегодня день рождения! Поздравляю, Илья!
Forwarded from Дежурный DevOps
Скорее бы 5G. Хочу уже увидеть JavaScript фреймворки по гигабайту и рассуждения, что просто по другому сделать нельзя.
#prog #cpp #rust #article
В относительно недавней статье автор вопрошает, заслуживает ли C++ те плохие вещи, которые о нём говорят. Ответная статья показывает, что таки заслуживает.
В относительно недавней статье автор вопрошает, заслуживает ли C++ те плохие вещи, которые о нём говорят. Ответная статья показывает, что таки заслуживает.
Blogspot
Does C++ still deserve the bad rap it has had for so long?
Traditionally C++ has been seen by many (and you know who you are) as just plain bad: the code is unreadably verbose, error messages are und...
Хочется написать compile-time крестики-нолики. Самое страшное, что я даже представляю, как это сделать, причём и на const fn, и на типах.
#prog #article #java
Статья о маленьких оптимизациях в Java (и в стандартной библиотеке, и в JVM). Статья интересная сама по себе, но я хочу обратить ваше внимание на проблемы в
Статья о маленьких оптимизациях в Java (и в стандартной библиотеке, и в JVM). Статья интересная сама по себе, но я хочу обратить ваше внимание на проблемы в
computeIfAbsent
и removeIf
, связанные с неограниченным алиасингом указателей.Хабр
Маленькие оптимизации в Java 9-16
Когда выходит новая версия Java, главные фичи всегда активно обсуждают. Но есть и работа, которая обычно остается «невидимой»: небольшие оптимизации в стандартно...
Forwarded from 🇺🇦 Go performance channel (Oleg Kovalov)
proposal: cmd/compile: switch to a register-based calling convention for #golang functions
Preliminary experiments indicate this will achieve at least a 5–10% throughput improvement across a range of applications
is accepted! 🚀
https://go.googlesource.com/proposal/+/master/design/40724-register-calling.md
https://github.com/golang/go/issues/40724
Preliminary experiments indicate this will achieve at least a 5–10% throughput improvement across a range of applications
is accepted! 🚀
https://go.googlesource.com/proposal/+/master/design/40724-register-calling.md
https://github.com/golang/go/issues/40724
GitHub
cmd/compile: switch to a register-based calling convention for Go functions · Issue #40724 · golang/go
I propose that we switch the Go internal ABI (used between Go functions) from stack-based to register-based argument and result passing for Go 1.16 1.17. I lay out the details of our proposal and t...
Блог*
#prog #rust #python #rustlib #amazingopensource Следующий логический шаг: макрос ct_python, который на этапе компиляции выполняет заданный код на Python и раскрывается в вывод в stdout этого скрипта. Презентация возможностей Документация
#prog #rust #c #rustlib #amazingopensource #menacingopensource
Макрос для запуска кода на Cна этапе компиляции из исходника на Rust. В рантайме, увы. (thanks @teamerlin за источник, thanks @ihatereality за поправку)
Анонс
Исходники
Макрос для запуска кода на C
Анонс
Исходники
Twitter
Ivan Enderlin 🦀
Oops… C in Rust. More to come :-). Please @rustlang, don't block me 🙏! #rustlang #c #cpp #clang
Forwarded from мне не нравится реальность (waffle 🧇🍓)
RFC: Reading into uninitialized buffers наконец-то смерджили!
GitHub
Tracking Issue for RFC 2930 (read-buf) · Issue #78485 · rust-lang/rust
This is a tracking issue for the RFC "2930" (rust-lang/rfcs#2930). The feature gate for the issue is #![feature(read_buf)]. This is now called BorrowedBuf rather than ReadBuf. About track...