1.83K subscribers
3.31K photos
132 videos
15 files
3.58K links
Блог со звёздочкой.

Много репостов, немножко программирования.

Небольшое прикольное комьюнити: @decltype_chat_ptr_t
Автор: @insert_reference_here
Download Telegram
#prog #article #amazingopensource

Алгоритм валидации UTF-8 практически без условных переходов, использующий меньше одной процессорной инструкции на байт входных данных. Реализация доступна как часть библиотеки simdjson
Forwarded from <илья as Человек> (ilya sheprut)
кстати, а ведь vi/vim хорошо звучит как нейтральная версия на замену множественному числу they/them
#prog #rust

Хозяйке на заметку

Пара советов по строкам в Rust:

1) Если вам нужно разбить строку по одному из нескольких возможных символов — не спешите расчехлять регулярки, для это задачи вполне хватит стандартной библиотеки. Множество строковых методов навроде {, r}split{, _terminator}, trim{, _start, _end}_matches, find и прочие принимают в качестве аргумента для поиска значение, тип которого реализует пока нестабильный трейт Pattern. В настоящий момент его реализуют &str, &&str, &String, impl FnMut(char) -> bool и (почему-то малоизвестный) &[char]. Таким образом, разбить строку по нескольким символам легко:

let result = "Hello, world!".split(&['o', 'l'][..]).collect::<Vec<_>>();
assert_eq!(result, vec!["He", "", "", ", w", "r", "d!"]);

2) Если функция принимает на вход &mut std::str::Chars, что она может с ним сделать?

Вызвать `next`?

Нет.

Также вызвать `next_back`?

Нет.

Это всё неполные ответы. Если мы получаем мутабельную ссылку на Chars, мы можем редактировать произвольным образом, в том числе и поменять его целиком. Chars внутри себя содержит строки, символы которой он перебирает, и при помощи метода Chars::as_str эту строку можно достать. Таким образом, имея мутабельную ссылку на Chars, можно вытащить из него строку, вырезать из строки нужный кусок и переписать переданный итератор .chars() от этого кусочка.

Покажу на примере.

Вот первый способ вытащить строку из Chars (медленный, требующий аллокаций и не совсем корректный):

fn extract_line2(chars: &mut Chars) -> String {
chars.take_while(|&ch| !matches!(ch, '\r' | '\n')).collect()
}

Второй способ (побыстрее и не требующий аллокаций):

fn extract_line<'s>(chars: &mut Chars<'s>) -> Option<&'s str> {
let s = chars.as_str();
let line = s.lines().next()?;
*chars = s[line.len()..].chars();
Some(line)
}
#prog #article

Статья об антипаттерне использования File.exists.

да, это статья десятилетней давности, ну и что?
#prog #article

Статья о Cyclone и его влиянии на другие языки программирования. Ни разу не полная, а скорее обзорная.

Есть перевод, но он мне показался малость кривым.
Блог*
Донаты! Донаты?
Я, конечно, совсем ни на что не намекаю, но вот вам номер моей карты:
4274 3200 5402 8520
Блог*
Я, конечно, совсем ни на что не намекаю, но вот вам номер моей карты: 4274 3200 5402 8520
Не переживайте, я не собираюсь возводить paywall, все посты в обозримом будущем продолжат публиковаться на канале
Forwarded from мне не нравится реальность (waffle 🧇🍓)
Хвастаюсь: мой PR добавляющий as_str методы к разным Split-строковым итераторам недавно смерджил толян!

[Tracking Issue]
Я ведь уже говорил, что Вафель молодец?
Как же достали люди, которые носят маски, но при этом не прикрывают нос. Они что, правда не понимают, что это всё равно что без маски ходить?
В Rust нельзя взять срез от слайса в константном выражении, АААААААААААА
Да, я действительно это реализовал. И напишу, как (в отличие от одного рельефного печенья)
#prog #zig #article

Статья о том, как можно использовать zig cc для простой кросс-компиляции программ на C. Вместе с подробностями о реализации.
#prog #rust #моё

UPD (22.09.2022): пост несколько устарел, держи поправку.
UPD (23.09.2022): как оказалось, я прилично так переусложнил реализацию. Ссылка на итоговый гист показывает последнюю версию. Ссылка на версию на момент написания поста: тык.

Сколько растоманов нужно, чтобы вкрутить лампочку? То есть, я хотел сказать, сколько nightly-фич нужно, чтобы перевести число в строку на этапе компиляции? Как оказалось... Ни одной!

Но обо всём по порядку. Пусть у нас есть константа, и мы хотим перевести её в строку на этапе компиляции. Фигня вопрос:

const STRING: String = 42.to_string();

Всё, всем спасибо за внимание, пост можно зака...

error[E0015]: calls in constants are limited to constant functions, tuple structs and tuple variants
--> src/lib.rs:1:24
|
1 | const STRING: String = 42.to_string();
| ^^^^^^^^^^^^^^

А, ну да. Я совсем забыл, что Rust создан не для программирования — он создан для страданий. Ну, окей, перевод числа в строку на этапе компиляции — это невероятно часто используемая фича, наверняка в стандартной библиотеке есть что-то готовое. И, действительно, есть stringify!

const STRING: &str = stringify!(42);

fn main() {
assert_eq!(STRING, "42");
}

Так, теперь точно можно заканчивать. Конечно, хардкодить константы нехорошо, так что дадим ей имя:

const THE_ANSWER: usize = 42;
const STRING: &str = stringify!(THE_ANSWER);

fn main() {
assert_eq!(STRING, "42");
}

Да, всё работает, всем спасибо, до новых встре...

thread 'main' panicked at 'assertion failed: `(left == right)`
left: `"THE_ANSWER"`,
right: `"42"`', src/main.rs:5:5

Погодите, что?

Так, дайте мне погуглить разобраться... Ага... Угу...

Итак, это не работает. stringify! работает исключительно на синтаксическом уровне. Так что же, мы лишены нормального инструмента? Строго говоря, да... Но мы можем сделать свой инструмент!

Для начала давайте разберёмся, что вообще такое строка? Строка — это массив байт. Не совсем произвольный — в Rust это должен быть массив байт, соответствующих кодировке UTF-8. С другой стороны, для записи чисел нам хватит и символов ASCII, так что на это можно немного забить. Так можем ли мы перевести число в массив байт? Конечно! И с учётом того, что c версии Rust 1.46.0 мы можем использовать if, match, while и loop в const fn, мы можем провести эту манипуляцию на этапе компиляции! Давайте посмотрим, как это сделать, на примере... Ну, скажем, u32:

const fn to_ascii(mut n: u32) -> [u8; 20] {
let mut ret = [0; 20];
let mut i = 0;
while n != 0 {
ret[i] = (n % 10) as u8 + b'0';
n /= 10;
i += 1;
}
...

Окей, пока всё идёт неплохо — выделить цифру несложно, равно как и перевести её в символ ASCII. Но у нас строка получилась в обратном порядке. Окей, давайте обратим порядок — достаточно лишь вызвать reverse:

...
ret[..i].reverse();
...

Теперь нам надо лишь...

error[E0723]: mutable references in const fn are unstable
--> src/lib.rs:12:5
|
12 | ret[..i].reverse();
| ^^^^^^^^
|
= note: see issue #57563 <https://github.com/rust-lang/rust/issues/57563> for more information
= help: add `#![feature(const_fn)]` to the crate attributes to enable

Ну ёлки-палки, компилятор продолжает ставить палки в колёса. Выходит, надо писать переворот вручную. И задействовать <[u8]>::swap мы тоже не можем, потому что эта функция, очевидно, также требует мутабельной ссылки (а ещё потому, что она не const fn, но это уже не имеет значения). Стиснув зубы, пишем переворот в очень сишном стиле:

...
let len = i;
let mut i = 0; // да, я дважды использую одно и то же имя
// для разных счётчиков, останьте
while i < len / 2 {
let tmp = ret[i];
ret[i] = ret[len - i - 1];
ret[len - i - 1] = tmp;
i += 1;
}
...
Теперь нам надо вернуть массив из функции и...

warning: this looks like you are swapping elements of `ret` manually
--> src/lib.rs:14:9
|
14 | / let tmp = ret[i];
15 | | ret[i] = ret[len - i - 1];
16 | | ret[len - i - 1] = tmp;
| |______________________________^ help: try: `ret.swap(i, len - i - 1)`
|
= note: `#[warn(clippy::manual_swap)]` on by default
= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#manual_swap

Нет, Clippy, я не могу тут вызвать reverse, я пытался! Когда ж ты поумнеешь? А пока что — вот тебе кляп:

...
#[allow(clippy::manual_swap)]
while i < len / 2 {
...

На чём я остановился? Ах да, мы возвращаем массив из функции и... Погодите, а как мы узнаем, где заканчивается строка? Сделать слайс не получится, потому что тогда мы будем возвращать ссылку на локальную переменную, а бровей чекер borrow checker бдит даже в const fn, зараза этакая. Выходит, нам надо возвращать вместе с массивом длину строки:

...
(ret, len)
}

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

const fn to_ascii(mut n: u32) -> ([u8; 20], usize) {
...

Как нам теперь из этого добра получить &str? Дело нехитрое: делаем слайс байтов, переводим в str — и дело в шляпе! Вернее, было бы в шляпе, если бы std::str::from_utf8 была бы const fn. А она таковой не является! Что же делать? Ладно, пока я над этим думаю, давайте проверим, что to_ascii работает правильно — хрен с ними, с константами, будем сами в строку переделывать:

fn main() {
for &x in &[42, 1, 0] {
let (bytes, len) = to_ascii(x);
println!("{}: {:?}", x, std::str::from_utf8(&bytes[..len]).unwrap());
}
}

Что оно выводит?

42: "42"
1: "1"
0: ""

Ну, по крайней мере, оно хотя ра... Стоп, что?

0: ""

Граничный случай! Окей, не вопрос, добавим if в начале:

    ...
if n == 0 {
return (ret, 1);
}
...

Теперь всё работает пра...

42: "42"
1: "1"
0: "\u{0}"

А, логично, нам нужен именно ASCII-шный символ нуля, а не нуль-символ:

    ...
if n == 0 {
ret[0] = b'0';
return (ret, 1);
}
...

Вот теперь всё в порядке:

42: "42"
1: "1"
0: "0"

Ладно, это всё прекрасно, но что нам делать с превращением всего этого в строку? Ну, раз честным путём пройти не вышло — считерим! В настоящий момент &str и &[u8] имеют одно и то же представление, так что, хотя мы и не можем использовать str::str::from_utf8_unchecked напрямую (это потребует nightly-фичи const_str_from_utf8_unchecked), мы можем просто скопировать реализацию (кстати, по не вполне понятным мне причинам transmute можно использовать внутри константных выражений, но не внутри const fn):

const DECOMPOSED: ([u8; 20], usize) = to_ascii(42);
const STR: &str = unsafe { std::mem::transmute(&DECOMPOSED.0[..DECOMPOSED.1]) };

Что ж, теперь мы можем спокойно распеча...

error[E0015]: calls in constants are limited to constant functions, tuple structs and tuple variants
--> src/lib.rs:29:49
|
29 | const STR: &str = unsafe { std::mem::transmute(&DECOMPOSED.0[..DECOMPOSED.1]) };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^

...Облом. Опять. Что ж делать-то? Видимо, придётся читерить ещё больше. Если мы не можем сконструировать слайс, то мы можем сконструировать что-то, что имеет такое же представление, как и слайс, и потом универсальным превращателем transmute преобразовать в строковой слайс. Но тут мы ступаем на шаткую территорию: хотя и известно, что слайс — это пара из указателя и длины, мы не знаем, в каком порядке они идут. Что ж, мы подсмотрим, какой порядок на самом деле и сделаем так же:

#[repr(C)] // repr(C) обязателен, чтобы между полями не было паддинга
struct RawSlice {
ptr: *const u8,
len: usize
}
🔥1