Telegram Web Link
Собственно история с Raspberry Pi показывает, что история опенсорца в железе не работает. Из-за кризиса чипов малины начинают замещаться всякими этими Khadas, Odroid и прочими, свято место пусто не бывает - десятки уже их, и потребитель в принципе не замечает, разве что цены моментально повысились (а на что сейчас не повысились?).

Беда, что RPi Foundation делала не только одноплатные компьютеры, а в том числе вычислительные модули для 3rd party, на которых за несколько лет пионэры умудрились наклепать soho-PLC моделей, участь которых печальна - практически все моментально уехали в EOL. Часть пионэров, выскребая последние деньги из карманов, перешли на in-house решения, часть просто сидит без компонентов и думает идти в управдомы.

В этом разница между софтом и железом. Если автор библиотеки забил на нее хер - вы всё равно можете пользоваться старой версией неограниченно долго, в крайнем случае форкнув её и поправив пару строк, починив баги и закрыв уязвимости. В случае железа, если поставщик умер - ваш бизнес моментально сосёт. Не смотря на то что весь техпроцесс открыт - копировать всё придётся с нуля.
👍12👎1
Промышленный интерфейс должен быть таким, чтобы туда лишний раз не хотели лазить и что-то ломать.

Первым делом нужно выбрать подходящую цветовую гамму. Зелёным по красному - почти идеально.
👍10😁7🔥5
Допустим вы захотели сделать сервис, который постоянно читает stdin, как сервисы в известном supervisord. Но внезапно что-то идет не по плану и появляются проблемки.

Дело в том, что рантаймы очень ревниво относятся к чтению-записи стандартных пайпов процесса и всё, что вы получаете в виде stdin/stdout - покрыто некоторым количеством локов, чтобы треды не читали и не писали туда одновременно - если локов не будет, а в stdin придут данные, кому их отдать?

Эта забота, в свою очередь, приносит беды. Так в tokio, например, future, который читает stdin, будет канселлиться на выходе примерно непредсказуемое время, а в Python вы просто получите на выходе некрасивую ошибку о том, что sys.stdin is locked.

Что делать? Единственный, он же официально рекомендуемый, workaround - открывать /dev/stdin как файл и читать его напрямую, а то что его читает только один тред одновременно - это уже на вашей совести.
👍8
Сегодня 21 день как работает панель которую я запустил на тестовом браузере. Почему-то если взять голый webkit и завернуть в Rust с тремя функциями управления снаружи - оно прекрасно работает, без утечек памяти и прочей херни. При том, что приложение примерно каждую секунду получает пуши в вебсокеты и делает каждые 10 сек кучу ajax-запросов на фоне. На панели 420 мегабайт доступной памяти, так что утечки бы уже давно вылезли.

Вопрос - каким местом пишутся все эти гугл-хромы? Я понимаю что функционала там намного больше, чем голый вебкит, но ведь последний и есть основное, а то что вокруг - периферия и никаких rocket science.
👍13🔥3
Наше
👍26😁6💩2
Segment@tion fault
Допустим вы захотели сделать сервис, который постоянно читает stdin, как сервисы в известном supervisord. Но внезапно что-то идет не по плану и появляются проблемки. Дело в том, что рантаймы очень ревниво относятся к чтению-записи стандартных пайпов процесса…
Тут коллега замечает, что обычно /dev/stdin читать простому юзеру нельзя. Да, именно так, но когда процесс запускается, дескриптор на stdin у вас уже есть.

В Rust его можно получить из libc::STDIN_FILENO, а в питоне из sys.stdin.fileno(). А дальше уже делайте с ним, что желаете. Обычно он = 0, но лучше подстраховаться.
👍6
Философская история произошла сегодня. У нас в подъезде есть лавочка, куда все приносят, что жалко выбросить и отдают бесплатно.

И вот сегодня вижу DVD audio player от панасоника. Я такие штуки очень люблю, особенно если прямо написано "audio" - там реально какой-то интересный hi/fi внутри и всё такое. Но через пару минут разочаровался и отнес назад.

Причина? Чуть менее чем все разъемы пропиетарны. Даже разъем питания. По сути, стандартные только audio-in и вход для FM. Я более чем уверен, что шины панаса очень качественные, но я в них ничего не понимаю, никогда не видел и мне лень в этом разбираться.

Какой можно сделать общий вывод? Не изобретайте велосипеды и суйте побольше стандартов в свои продукты. Потому что лучшее в мире, но совершенно непонятное - большинству не нужно даже даром.
👍23
С торговыми марками проблем будет больше и больше, в том числе в опенсорце и даже в пет-проектах. Потому что экономика укрупняется, глобализируется, а патентное право живёт в прошлом веке (если не в позапрошлом). Я уже ругался, что практически всё IT свалено в class 9 и немножко в 42й, хотя например узкоспециализированная база данных, которой пользуется 1000 человек во всём мире и какая-нибудь видеокарта - две большие, огромные разницы.

И тут ничего не поделать - каждый хочет назвать свой продукт емко, метко и красиво, а таких названий всё меньше и меньше. Выхода может быть два, и нужны оба:

- следует изменить понятие "похожая торговая марка" - RULEX и RELEX могут звучать очень похоже, но юзер не дебил, в наш век отличит и по написанию

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

p.s. ELBUS кстати перед релизом тоже будет переименован, как собирались. Потому что торговая марка ELBUS - уже занята какой-то полумертвой шиной-филдбасом, про которую полтора упоминания в гугле, зато есть записи в патентных бюро. О названии сообщу через месяца полтора и переименую крейты, как только нашу марку зарегистрируют.
👍9
В новом Rust-Clippy наконец есть lint format_push_string, который меня бьет по рукам за дурную привычку - конкатенацию String и &format!()

При таком подходе format!() выделяет память на отдельную строку, потом прибавляет её к имеющейся и освобождает память, что нехорошо. А хорошо использовать write!(). Чего и вам советую

Вот такими мелочами среднестатистический растокод работает быстрее, чем среднестатистический сишный. Потому что одно дело писать код, а другое - писать его хорошо. Но в сишке по рукам эффективно бить некому.
👍13
Media is too big
VIEW IN TELEGRAM
Китайские электрики принципиально игнорируют шины и клеммы, поэтому часто смотрю их каналы чтобы подивиться мастерству

Не дай бог, конечно
😁18👎1
Наши любимые коммиты

a1e23f9c (origin/HEAD) fix - спешил
2742aeb2 fix:wq - очень спешил
😁12💩1
Rust под шиндовс - это видимо месть виндоузятникам за годы унижений.

Начиная что инсталлер находится где-то в жопе мира. Дальше крейты

- поставьте curl
- и tar
- не этот, другой
- хочу perl
- у вас perl делает LF, хочу CRLF
- а мне норм было. верни LF
- я вообще под шиндовс не работаю

в общем, большой респект виндовс прогерам. я бы уже застрелился
😁23👍4👎2🔥1
Я решил написать гуй.

В общем-то я делаю это крайне редко и перекладываю эту почетную задачу на других, но это - ответственный гуй, и я решил взяться за дело сам. К гую я предъявлял следующие требования:

- Возможность вставлять WebView, но выборочно, а не на полную морду
- Кросс-платформенность хотя бы на уровне Вин-Лин. А лучше Вин-Лин-Мак
- Вменяемый внешний вид и работа
- Поддержка из коробки деревьев и желательно таблиц
- обязательно Rust, причем не в каком-то там WASM-контейнере, а настоящий

В финал с практическим созданием прототипа вышли следующие кандидаты:

- wxWidgets
- FLTK
- GTK
- Qt

И вот какие выводы могу сделать

- wxWidgets - биндинги под Rust вроде есть и выглядит неплохо. Но биндинги ужасные, автогенеренные, с кучей си-подобных констант. wxRust давно заброшен, а wxRust2 на crates.io не публикуют видимо из принципа. Не нашел биндинг на wxTreeCtrl и решил дальше даже не смотреть.

- FLTK - лайк редакции за самые лучшие биндинги. На этом лайки заканчиваются. Сам по себе FLTK работает неплохо и вполне отлично справляется с простыми интерфейсами. Пока вам не нужно resizable и вы не начинаете жонглировать элементами на ходу. Еще у FLTK довольно скромные таблицы из коробки, поэтому сами авторы раст-крейта рекомендуют использовать fltk_table. Который в свою очередь изобилует чудесами аля std::sync::Mutex::try_lock(..).unwrap(), чтобы удобно падать в корку, а при попытке замены try_lock на lock периодически вешается. FLTK я смотрел и пытался привести в чувства дольше всех, но увы. Еще лайк за лицензию, которая позволяет вкомпиливать в пропиетарщину.

- GTK и Qt. Внезапно, но да. Первому 24 года, а второму 27. И они до сих пор уделывают рынок. Не заметил проблем ни с первым, ни со вторым, и неудивительно - за четверть века холивар гноме vs кеды сделал своё дело и оба фреймворка от детских болезней давно избавлены. GTK подкупает лицензией, которая тоже позволяет вкомпиливать в пропиетарщину, Qt требует либо делать опенсорц, либо платить денег.

По практике. Минусы GTK - страшненький. Плюсы - более лучшие биндинги для Rust. Минусы Qt - все биндинги автогенеренные, но вменяемые, хотя некоторых не хватает. Плюсы - подкупает внешним видом и наличием qt-designer для мышевозения, а недостающие биндинги в принципе легко доделать.

В общем, вердикт такой. Если вы можете отрелизить гуй под GPL или у вас есть деньги - возьмите Qt и не парьтесь. Иначе берите GTK. Остальные можете даже не смотреть.
👍26
А теперь немножко из практики использования Rust + Qt (5): да, вполне можно и даже удобно, но есть некоторые nuts & bolts.

- Биндинги берем qt_core, qt_widgets и прочее с crates.io - они юзабельные, а что неюзабельное - доделываем костылями (например нет проц-макросов для slot QTreeWidgetItem и подобных, заменяем на пустой а потом в дереве руками берем current_item и т.п.)

- При построении динамического-чего-угодно, помним, что Rust освобождает память как только объект выходит из видимости, а таблицы-деревья и прочее в Qt оперируют указателями. Тоесть храним элемент дерева или таблицы в локальном например Vec, а Qt отдаем поинтер. При очистке, важно - сначала чистим Vec, потом убираем ненужные поинтеры из структуры Qt. Наоборот - будет падать. У деревьев всё чистим только снизу вверх.

- У Qt своя система сигналов и потоков, к которой нужно привыкнуть. _ttps://github.com/jlgerber/rust-qt-conductor - вот тут нашел хороший пример, как коллега игрался, и оно даже работает. Но для интерфейсов, где обычно реал-таймы не нужны, можно заменить push на pull - антиканонично, но зато на 5 строчек: засовываем задачи из фоновых потоков в обычный sync-канал, а потом в главном используем QTimer, который каждые 50-100мс дергает try_recv

- Примеров - очень мало, а доки по биндингам в основном отправляют на хелпы от С++. Это печально, но не критично

- Всё, для чего нет биндингов - либо генерится руками, либо биндится на QWidget, а потом в С++ элементарно раскастовывается на отдельные функции с нужным типом. Например, QWebView:

// ++
#include <QtWebKitWidgets/QWebView>
#include <QtWebKitWidgets/QWebFrame>
#include <QUrl>

extern "C" void qwebview_load(QWebView *view, char *url) {
view->load(QUrl(url));
}

extern "C" void qwebview_eval(QWebView *view, char *code) {
view->page()->mainFrame()->evaluateJavaScript(QString(code));
}

// Rust
extern "C" {
fn qwebview_load(view: *mut QWidget, url: *const c_char);
fn qwebview_eval(view: *mut QWidget, code: *const c_char);
}

- Qt Designer - весч. Подружились, при всей моей нанависти к любым гуям.
👍12
#ТипичныеКресты, как раскрасить ячейку в таблице Qt

- создайте объект QColor
- вставьте его указатель в QBrush
- вставьте указатель на QBrush в объект ячейки
- вставьте ячейку в таблицу
- не вздумайте ничего дропать, пока таблица ссылается на ячейку

В Rust встретить такую конструкцию шансы примерно такие же, как встретить динозавра на улице.

Во-первых, цвет любой автор бы закодировал в 4-5 байт (пятый - код имени константы), а точнее бы ничего сам не кодировал а просто сделал enum с вариантами аля rgb, rbga, hsv, name: &'static str и прочими.

Во-вторых, единственное тяжелое, что есть в QBrush - опциональная texture image, которая явно не меняется внутри и моментально просится как static или rc.

В третьих, увидев такой бутерброд из указателей, борроу-чекер моментально бы потребовал бутерброд из lifetimes. А представив такой бутерброд из lifetimes, автор бы пожалел и себя, и потенциального юзера крейта и устроил бы либо передачу всех данных как owned, с последующим при необходимости get/get_mut, либо завернутых в rc, возможно с мьютексом, тут уже как угодно.

Rust заставляет думать и о себе, и о других. Это и есть user-friendliness.
👍20👎2
Вы думаете шоппинг это легко? Типичный пример закупки:

- нам нужны 100 штук компутеров на DIN rail, требования такие-то
- да, конечно. вот то что почти соответствует вашим требованиям. Но оно не на din rail
- но нам надо на DIN rail
- это обязательно?
- да, обязательно
- всё равно возьмите этот

Иногда начинаешь понимать Джобса
👍10😁2
Зачем в Rust нужен unsafe, кроме как дергать сишные функции? Да потому что иногда компилятор сложную логику просто не понимает. К счастью, это случается редко, но рассмотрим такой очень утрированный пример:

Есть структура

struct Foo {
id: String,
entries: Vec<String>,
}

а у нас есть Vec<Foo>, с которым нужно сделать следующее: найти по id нужную структуру и вернуть mut на ее entries. Причем, если структура не существует - создать новую. Код будет выглядеть так

fn find_entries<'a>(id: &str, data: &'a mut Vec<Foo>) -> &'a mut Vec<String> {
for elem in data.iter_mut() {
if id == elem.id {
return &mut elem.entries;
}
}
data.push(Foo {
id: id.to_owned(),
entries: Vec::new(),
});
&mut data.last_mut().unwrap().entries
}

и закончится тем, что борроу-чекер его завернет, объясняя нам, что first mutable borrow произошел, когда мы делали return в цикле, а second - когда мы мучали data и добавляли недостающий элемент

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

Что может быть весьма неприятно, если структуры у нас сложные, а их еще и несколько вариантов. Другой вариант - не возвращать мутабельный указатель на вектор, а послать данные прямо в функцию и пусть она их отпроцессит. Но это тоже не всегда возможно.

Пока борроу-чекер не умеет сложный анализ, его можно немножко обмануть, скрыв явную мутабельность. Изменим код вот так:

unsafe fn as_mut<T>(reference: &T) -> &mut T {
let mut_ptr = reference as *const T as *mut T;
&mut *mut_ptr
}

fn find_entries<'a>(id: &str, data: &'a mut Vec<Foo>) -> &'a mut Vec<String> {
for elem in data.iter() {
if id == elem.id {
return unsafe { &mut as_mut(elem).entries };
}
}
let data = unsafe { as_mut(data) };
data.push(Foo {
id: id.to_owned(),
entries: Vec::new(),
});
&mut data.last_mut().unwrap().entries
}


и борроу-чекер двойную мутабельность уже не заметит.
👍8💩7🔥1
жиза
😁34👍4👎2
Segment@tion fault
Зачем в Rust нужен unsafe, кроме как дергать сишные функции? Да потому что иногда компилятор сложную логику просто не понимает. К счастью, это случается редко, но рассмотрим такой очень утрированный пример: Есть структура struct Foo { id: String, …
В борьбе за безопасный код в комментах разразилась небольшая дискуссия, самый лучший ответ (как часто бывает) предложил @insert_reference_here.

Поскольку компилятор пока не умеет отдать нам mutable дважды, не смотря на то что реально мы берем единожды - давайте немного изменим подход:

- сначала ищем позицию элемента в векторе, при этом вектор у нас остаётся immutable
- в зависимости от результата, выполняем либо

блок А) берем data как mutable, элемент по позиции и возвращаем ссылку на его поле entries
блок Б) берем data как mutable, вставляем новый элемент и работаем с ним

fn find_entries<'a>(id: &str, data: &'a mut Vec<Foo>) -> &'a mut Vec<String> {
if let Some(pos) = data.iter().position(|elem| elem.id == id) {
&mut data[pos].entries
} else {
data.push(Foo {
id: id.to_owned(),
entries: Vec::new(),
});
&mut data.last_mut().unwrap().entries
}
}

При таком подходе код получается полностью safe и с минимумом overhead.
👍17
правильный энтырпрайз
😁19
2025/10/01 06:31:11
Back to Top
HTML Embed Code: