Pull to refresh
1
0
Алексей Миклин @miklin-ag

User

Send message

А все языки с ручными управлением памятью зарание и гарантированно уступают всему ранее рассмотренному.

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

ручное жонглирование бесконечными borrow/drop/upgrade/clone тратит больше времени и сил, чем собственно решение задачи.

Вызовы borrow стоит спрятать в методы реализации, погрузив Rc<RefCell> в обёртку, чтобы потроха не торчали на клиенте. - Тогда и drop не понадобится. А upgrade это аналог lock в C++, так что здесь странная претензия именно к Rust, а clone - явное дешёвое копирование shared pointer-а - это как раз базовая вещь в Rust - явное клонирование в противовес неявному в плюсах.

Если, как полагается, прятать детали реализации, то можно не трогая клиент заменять реализацию на другие варианты, например, на арену с хендлами вместо указателей.

А выставить из обёртки удобно методы, либо возвращающие обёрнутые объекты с shared pointer внутри, либо принимающие коллбэк в виде замыкания для чтения либо модификации сложных объектов - например, для чтения или модификации стиля, что у вас делалось через style.clone_resized(style.size + 1.0).

Это показывает противоречие в философии языка:

  • безопасность без падений остается недостижимым идеалом,

В чём же противоречие? Rust не декларирует безопасность без паник, хотя и без них тоже можно. Паники обычно применяются как раз для недопущения UB либо при нарушении инвариантов.

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

Непонятно, почему минусы - человек поделился идеей (и неплохой) и своей реализацией - только за это уже молодец, разве нет?

Так не скомпилируется в Rust:

pub struct Calc {

lib: Library,

add: Symbol<CalcFn>,

Здесь libloading::Symbol должен иметь lifetime в списке параметров дженерика, то есть рабочим был бы вариант:

pub struct Calc<'a> {
  add: Symbol<'a, CalcFn>,

Здесь 'a это lifetime от lib, который в этом случае передаётся в load:

impl<'a> Calc<'a'> {
  pub fn load(lib: &'a Library) -> anyhow::Result<Self> 

При этом сам lib инициируется выше по стеку перед Calc.

Всё это из-за строгой гарантии на этапе компиляции, что полученные указатели на функции не протухнут, когда библиотека выгрузится.

Альтернативой вынесению lib из Calc было бы использовать unsafe: либо для self-referential struct, либо для приведения lifetime 'a к 'static для Symbol.

Как говорится, добро пожаловать в Rust :)

Отличная статья, спасибо, всё доступно и с деталями!

Небольшая описка:

Для процедурных макросов аналога $crate нет, поэтому используйте proc-macro-crate::crate_name("your-support"), а затем подставьте либо crate::…, если вы находитесь в нём же, либо реальное имя из Cargo.toml.

Это защищает от сценариев вида:

```

[dependencies]
your-support = { version = "0.1", package = "my-super-support" }

```

В макросе корректно получим my_super_support и сгенерируем ::my_super_support::path::to::fn.

Верно было бы так (если минимально править):

[dependencies]
 my-super-support = { version = "0.1", package = "your-support" }

А вообще понятнее было бы использовать переименованное название зависимости как например renamed-support.

Information

Rating
6,578-th
Registered
Activity

Specialization

Software Developer, Application Developer
Senior