Size: a a a

2020 November 15
Блог*
Если в поиске телеги на десктопе набрать тильду, то в строке поиска оно отобразится как дефис, но всё равно будет искать тильду в именах контактов/чатов/каналов
источник
Блог*
#video

Я хотел сделать интересный, содержательный пост про одну вещь на расте, но со мной случились полтора часа ночи и это. Прошу понять и простить.
источник
2020 November 16
Блог*
#prog #rust

Хотел написать, что это ещё и #meme, но ведь это чистая правда
источник
Блог*
Но зато они приходят ко всем остальным и гнобят их языки.
https://twitter.com/ctrlshifti/status/1327923586259181568
источник
Блог*
#prog #rust #article

Статья про то, как можно написать FFI-биндинги, сохранив при этом проверки borrow checker-а. К сожалению, без extern types сделать эргономично сложно.
источник
Блог*
#prog #rust #article

Статья про то, как писать код с инициализацией данных, сохранив сильную exception safety.
источник
Блог*
Что нужно делать при начале разработки тулзы по работе? Правильно, искать подходящее название в греческой мифологии.
источник
Блог*
#prog #rust #article

Статья об использовании PGO для rustc. В двух словах: улучшения есть, и весьма заметные, но пока что непонятно, как это можно интегрировать с CI
источник
Блог*
Тем временем @ihatereality делает что-то странное с @optozorax_dev. Что это? Может, он так заигрывает? 🤔
источник
2020 November 17
Блог*
#prog #rust #моё

Как сравнить в Rust две строки, игнорируя регистр символов? Строго говоря, используя лишь стандартную библиотеку — никак, поскольку перевод из одного регистра в другой зависит от локали, но давайте пока проигнорируем эту деталь и притворимся, что среди пользователей нашего приложения нет кого-то, кто живёт в Турции или Азербайджане (а также что у нас нет проблемы нормализования строк).

Итак, как же нам сравнить две строки, игнорируя регистр символов? Большинство Rust-программистов (особенно новичков) напишут что-то вроде этого:

fn equal_ignoring_case(a: &str, b: &str) -> bool {
   a.to_lowercase() == b.to_lowercase()
}

Правильное ли это решение? НЕТ, НЕПРАВИЛЬНОЕ, КТО ВООБЩЕ ТАК ПИШЕТ Технически оно верное, но оно делает много лишней работы. Что тут происходит? Сначала под первую строку выделяется место в куче, которое заполняется проходом по строке с преобразованиями по довольно нетривиальным правилам, во время которых выполняется бинарный поиск по захардкоженным таблицам, затем то же самое происходит для второй строки, и только после этого строки сравниваются друг с другом. Всё это происходит даже в том случае, если строки большие и даже если строки различаются уже первым символом.

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

fn equal_ignoring_case(a: &str, b: &str) -> bool {
   a.chars().flat_map(char::to_lowercase).eq(b.chars().flat_map(char::to_lowercase))
}

(тут используется почему-то малоизвестный метод Iterator::eq, который проверяет, что два итератора выдают равные последовательности элементов)

А что делать, если одна из строк заведомо содержит лишь ASCII символы (например, это литерал для какого-нибудь формата разметки)? В таком случае мы можем игнорировать юникодные правила преобразования и проверять лишь равенство ASCII-символов без учёта регистра, что значительно более простая задача. Мы можем эксплуатировать тот факт, что строка — это набор байт, проверять строки побайтово, перед этим ещё и проверить, что у них одинаковая длина... Или же не переизобретать велосипед и воспользоваться готовым методом str::eq_ignore_ascii_case.

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

fn starts_with_ignoring_case(s: &str, prefix: &str) -> bool {
   s.to_lowercase().starts_with(&prefix.to_lowercase())
}

, но оно не оптимально по тем же причинам. К сожалению, трюк с Iterator::eq здесь не подойдёт, потому что этот метод может вернуть true в том случае, если s короче prefix и потому не может его содержать. По аналогичным причинам не подойдёт Iterator::zip. К сожалению, придется написать немного кода самому, с ручными вызовами next:

fn starts_with_ignoring_case(s: &str, prefix: &str) -> bool {
   let mut s = s.chars().flat_map(char::to_lowercase);
   let mut prefix = prefix.chars().flat_map(char::to_lowercase);
   while let Some(s_ch) = s.next() {
       match prefix.next() {
           Some(p_ch) => if s_ch != p_ch {
               return false
           },
           None => return true, //префикс закончился, а все символы до это совпадали
                                //значит, строка содержит префикс
       }
   }
   //закончились символы в строке, и они все совпали с символами в префиксе
   true
}

Разумеется, и тут применима оптимизация, если одна из строк состоит только из ASCII-символов:

fn starts_with_ignoring_ascii_case(s: &str, prefix: &str) -> bool {
   s.get(..prefix.len()).map_or(false, |start| start.eq_ignore_ascii_case(prefix))
}

Почему я решил об этом написать? Да потому что меня бесит, когда я вижу подобный настолько неоптимальный код. Так что можете считать, что это #бомбёжкипост.
источник
Блог*
источник
Блог*
#prog #rust #article

Статья о том, как апгрейд до новой версии tokio привёл к тому, что сервер, обычно отвечающий меньше, чем за миллисекунду, начал отвечать ровно за 40 миллисекунд. Спойлер: как всегда в подобных случаях, баг образовался из-за взаимодействия нескольких механизмов, включая части из linux.
источник
Блог*
dereference_pointer_there
#prog #rust #моё

Как сравнить в Rust две строки, игнорируя регистр символов? Строго говоря, используя лишь стандартную библиотеку — никак, поскольку перевод из одного регистра в другой зависит от локали, но давайте пока проигнорируем эту деталь и притворимся, что среди пользователей нашего приложения нет кого-то, кто живёт в Турции или Азербайджане (а также что у нас нет проблемы нормализования строк).

Итак, как же нам сравнить две строки, игнорируя регистр символов? Большинство Rust-программистов (особенно новичков) напишут что-то вроде этого:

fn equal_ignoring_case(a: &str, b: &str) -> bool {
   a.to_lowercase() == b.to_lowercase()
}

Правильное ли это решение? НЕТ, НЕПРАВИЛЬНОЕ, КТО ВООБЩЕ ТАК ПИШЕТ Технически оно верное, но оно делает много лишней работы. Что тут происходит? Сначала под первую строку выделяется место в куче, которое заполняется проходом по строке с преобразованиями по довольно нетривиальным правилам, во время которых выполняется бинарный поиск по захардкоженным таблицам, затем то же самое происходит для второй строки, и только после этого строки сравниваются друг с другом. Всё это происходит даже в том случае, если строки большие и даже если строки различаются уже первым символом.

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

fn equal_ignoring_case(a: &str, b: &str) -> bool {
   a.chars().flat_map(char::to_lowercase).eq(b.chars().flat_map(char::to_lowercase))
}

(тут используется почему-то малоизвестный метод Iterator::eq, который проверяет, что два итератора выдают равные последовательности элементов)

А что делать, если одна из строк заведомо содержит лишь ASCII символы (например, это литерал для какого-нибудь формата разметки)? В таком случае мы можем игнорировать юникодные правила преобразования и проверять лишь равенство ASCII-символов без учёта регистра, что значительно более простая задача. Мы можем эксплуатировать тот факт, что строка — это набор байт, проверять строки побайтово, перед этим ещё и проверить, что у них одинаковая длина... Или же не переизобретать велосипед и воспользоваться готовым методом str::eq_ignore_ascii_case.

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

fn starts_with_ignoring_case(s: &str, prefix: &str) -> bool {
   s.to_lowercase().starts_with(&prefix.to_lowercase())
}

, но оно не оптимально по тем же причинам. К сожалению, трюк с Iterator::eq здесь не подойдёт, потому что этот метод может вернуть true в том случае, если s короче prefix и потому не может его содержать. По аналогичным причинам не подойдёт Iterator::zip. К сожалению, придется написать немного кода самому, с ручными вызовами next:

fn starts_with_ignoring_case(s: &str, prefix: &str) -> bool {
   let mut s = s.chars().flat_map(char::to_lowercase);
   let mut prefix = prefix.chars().flat_map(char::to_lowercase);
   while let Some(s_ch) = s.next() {
       match prefix.next() {
           Some(p_ch) => if s_ch != p_ch {
               return false
           },
           None => return true, //префикс закончился, а все символы до это совпадали
                                //значит, строка содержит префикс
       }
   }
   //закончились символы в строке, и они все совпали с символами в префиксе
   true
}

Разумеется, и тут применима оптимизация, если одна из строк состоит только из ASCII-символов:

fn starts_with_ignoring_ascii_case(s: &str, prefix: &str) -> bool {
   s.get(..prefix.len()).map_or(false, |start| start.eq_ignore_ascii_case(prefix))
}

Почему я решил об этом написать? Да потому что меня бесит, когда я вижу подобный настолько неоптимальный код. Так что можете считать, что это #бомбёжкипост.
Я говорил это ранее и скажу ещё раз: Вафель умница. Вскоре после того, как я выложил этот пост, он опубликовал вариант starts_with_ignoring_case с более простой реализацией.
источник
Блог*
#prog #rust

Библиотека spirit — для снижения количества бойлерплейта при написании долгоживущих серверов. Логгирование, перечитывание конфигов при изменении, реагирование на сигналы, оборачивание в демоны — вот это вот всё.
источник
Блог*
#prog #rust #amazingopensource

Заметки о библиотеке для использования постфиксных макросов, с изложением мотивации для её создания, истории вопроса и немного о реализации.

use postfix_macros::{postfix_macros, unwrap_or};

fn main() {
   postfix_macros! {
       let urls = ["https://rust-lang.org", "http://github.com"];
       for url in urls.iter() {
           let mut url_splitter = url.splitn(2, ':');
           let scheme = url_splitter.next().unwrap();
           let _remainder = url_splitter.next().unwrap_or! {
               println!("Ignoring URL: No scheme found");
               continue;
           };
           println!("scheme is {}", scheme);
       }
   }
}
источник
Блог*
Мои знакомые — особенно те, кто сами ведут свой канал в Телеграм — спрашивают, как мне удаётся постить качественный контент каждый день.

Ответ очень прост: я выкладываю некачественный контент.
источник
Блог*
#psy

О том, как правильно извиняться. Написано в контексте брака, но, на мой взгляд, вполне применимо и в более общих ситуациях.
TL;DR: Извиняетесь, но не заставляйте просить прощения

В
этот раз ссылка на приватный канал в Telegram, так что вот вам сначала инвайт, а потом уже и ссылка на сам пост.
источник
Блог*
блять, опять я всё своё свободное время потратил в телегу и нихера не напрогал. Надо удалять её нафиг
источник
Блог*
источник
2020 November 18
Блог*
#prog #cpp

twitter.com/AffectiveCpp — лучший твиттер-аккаунт о C++. Полезен даже профессионалам, а новичкам так и вовсе практически обязателен.
источник