Comments 35
Вы игнорируете лучшее в Rust. Типажи бесплатны в бинарном виде, но позволяют описать ожидания от входного типа без учёта его конкретики. В зависимости от того, что важнее - скорость или размер бинарного файла, вы можете использовать динамические трейты (dyn traits) или статические. Статические быстрее, но увеличивают размер бинарного файла, динамические делают дополнительный lookup по таблице методов, зато не вызывают распухания из-за мономорфизации.
Использование пустых типов для усиления типизации - это одна из важных особенностей языка. Вы можете объявить типы без значений и требовать их себе на вход для обеспечения соблюдения протоколов вызова (например, можно иметь Token(), в отсутствтие которого вызов не возможен, а ownership которого позволяет автоматически избежать race conditions).
Мой совет, нырнуть глубже во все эти Vec'и и сплайсы - в них сила, даже если в финале у вас не будет Vec'а.
Спасибо! динамические типы dyn точно работают при отсутствии кучи?
fn f(x: &dyn std::fmt::Display) {
println!("{}", x);
}
fn main() {
f(&"test");
f(&1);
}
Я не совсем хорошо понимаю о чём я говорю, но dyn traits - это такой метод присобачить к типу dispatch table, которая устроена таким образом: пользователи под динамическими трейтами (которые принимают dyn trait) на уровне магии компилятора знают какая функция в какое смещенеи ведёт. Каждый провайдер динамического трейта (я не помню, каждого типа или каждого вызова функции с динамическим трейтом) записывает в соответствии с этой магией таблицу вызовов. Вроде бы, она создаётся во время компиляции и хранится в rodata.
Но, может быть, я несу ахинею, потому что я в Rust новичок.
Всё вы правильно написали, но можно написать проще: dyn traits — это такой метод присобачить к ссылке таблицу виртуальных функций. Поскольку ссылка может указывать куда угодно, а таблица виртуальных функций собирается один раз компилятором и хранится в rodata — никакой кучи не требуется.
Использование Result<> для возврата ошибок. Наверное это удобно, когда стек вызовов глубокий и активно применяется оператор “?”
Так же оператор ? можно использовать вместе с Optional, если ошибки не важны а нужно только наличие результата.
Умные книги советуют для унификации передачи кодов ошибок опять же применять Box<>, которого в embedded нет.
А ещё можно для этого использовать кастомный Enum, опционально для удобства обьявить его как ошибку (c помощью трейта Error) и оборачивать им все остальные ошибки.
enum MyErrorCode {
Failed = 1,
...
}
enum MyError {
CodeError(MyErrorCode),
ATError(ATLibraryError),
STMError(STMLibraryError),
...
}
impl Error for MyError {
...
}
Интересная мысль, попробую
Добавлю: если реализовать для MyError типажи From<ATLibraryError>
и From<STMLibraryError>
— оператор "?" учтёт возможность такого преобразования.
Что то пропустил видимо в статье, но не понял чем компилируется исходник на Rust и чем отлаживается?
Так и не увидел выводов: изначальный замысел в стабильности в embedded у вас вышел относительно С? Вообще слабое звено ч тут вижу SIM800, как и другие версии этих модулей.
У меня есть вопрос, а зачем использовать Rust в таком небольшом проекте? Тут и чистого си вполне достаточно. Не то чтобы я критикую, интересна мотивация.
Вопрос снят, нашёл в статье - из интереса и попробовать
Заметил вот такое высказывание из книги по ссылке
Panicking is a core part of the Rust language.
Похоже на что-то принципиально новое.
На расте просто меньше вероятность написать программу, приводящую к разрушению памяти
Не придётся, благо занимающаяся этим группа не сидела без дела рассказывая какой замечательный Rust сам по себе, а давно придумала PAC (Peripheral Access Crate).
Как и зачем оно устроено можно почитать тут: https://docs.rust-embedded.org/book/peripherals/index.html
Таки отличаются — если на железе нет MMU, то, скорее всего, никакого SIGSEGV не будет в принципе, и программа будет работать непредсказуемо.
А паника возникнет надёжно, и так же надёжно будет обработана, пусть даже вся обработка будет заключаться в аварийном останове.
У меня есть вопрос, а зачем использовать Rust в таком небольшом проекте?
А почему размер проекта имеет значение? Если бы код писался на С++, а не на расте вопрос всё равно возник бы? Ну или можно вывернуть другую сторону: "зачем использовать С в таком небольшом проекте, разве асма недостаточно?"
Признаюсь честно, я предвзят, но спрашиваю тоже без критики и агрессии. (:
В данном случае всё более менее норм, и против плюсов на МК ничего не имею, когда они применяются осознанно, с пониманием как это делать правильно и что можно а что недопустимо на МК, просто иногда веселит когда говорят, "мы тут решили помигать парой диодиков и прочитать ацп, с помощью ***какая-то новомодная фича***, но 256кБ это слишком мало для этого =)
п.с. можно сразу отдельный комп поставить на I7
SIM800L + STM32 Bluepill + Rust. Как оно?