LINUX.ORG.RU

Имеет ли смысл использовать Rust для написания библиотеки?

 ,


2

3

Есть желание написать одну библиотеку. Она может использоваться в разных языках и на разных платформах. В частности важно, чтобы не было проблем на Windows. Работать должна быстро, памяти потреблять мало. Логичный выбор - С. Использовать можно из любого языка, накладных расходов почти ноль.

Но про Rust много базза в последнее время и, т.к. проект just for fun, стало интересно, имеет ли смысл такое делать на нём? Готовы ли инструментальные средства для генерации DLL на всех платформах? Не будет ли проблем с интеропом (C++, Python, Objective C)? Какой результирующий объём бинарников, необходимых для распространения? Скажем, С-шная DLL-ка будет весить совсем немного, а стандартная библиотека есть практически на всех платформах.

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

★★★★★

Последнее исправление: Legioner (всего исправлений: 1)
Ответ на: комментарий от anonymous

Детали реализации, мне лень делать лучше.

А это вообще возможно? У тебя же отдельный trait и отдельный impl, которые ничего про внутреннее устройство TreeMap не знают и знать не хотят.

anonymous
()
Ответ на: комментарий от buddhist

Да, ЕМНИП Unit-ом зовётся. Но что ты понимаешь под нормальными я не знаю, вроде как все более-менее приличные программы на паскале разбивают на модули - по модулю на окошко, по модулю на элемент интерфейса, по модулю на отдельную логическую часть и т.д. Вот только я никак не пойму, зачем всё это надо в C и особенно в C++, т.к. есть заголовочный файл + файл кода, вот тебе и модуль (как отдельная программная единица, хоть в отдельную библиотеку компили, хоть в бинарик включай). А в C++ ещё и классы есть для разделения логических частей на объекты и функции для работы с ними, а ещё неймспейсы, когда есть смысл классы и функции в отдельную логическую группу собрать.

peregrine ★★★★★
()

Я так понял, что в Rust нет ни классического наследования, ни миксинов, ни какого-либо механизма делегирования реализации? Они предлагают копипасту?

anonymous
()
Ответ на: комментарий от anonymous

Я так понял, что в Rust нет ни классического наследования

Есть наследование трейтов.

ни какого-либо механизма делегирования реализации?

Чем тебе трейты не «механизм делегирования реализации»?

Они предлагают копипасту?

Странное предположение. Си предлагает копипасту?

tailgunner ★★★★★
()
Ответ на: комментарий от tailgunner

Странное предположение.

Ну вот есть у меня есть какой-то трейт. Есть реализация, которая меня практически полностью устраивает. Но я хочу переопределить один из методов. Что мне делать?

Если у меня есть наследование, я могу отнаследоваться(в случае C++ можно подумать как об открытом, так и о приватном наследовании, в зависимости от). Если у меня есть миксины, я могу включить реализацию и определить метод по-своему. В случае scala первые два варианта смешиваются. Если у меня есть делигирование, я могу завести себе поле, делигировать ему реализацию trait'а/интерфейса, а операцию переопределить. Так вроде было можно в Delphi и в одной из очень старых версий C++, до того как там появилось множественное наследование(описано в D&E).

anonymous
()
Ответ на: комментарий от tailgunner

Си предлагает копипасту?

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

anonymous
()
Ответ на: комментарий от quantum-troll

Это хорошо. В случае scala это поможет, поскольку там есть множественное наследование(с линеаризацией) trait'ов. Как это мне поможет в Rust'е?

anonymous
()
Ответ на: комментарий от anonymous

Ну вот есть у меня есть какой-то трейт. Есть реализация, которая меня практически полностью устраивает. Но я хочу переопределить один из методов. Что мне делать?

Если устраивающая реализация - дефолтная, то наследование трейта или агрегация; иначе - только агрегация.

Если у меня есть делигирование

Делегирование у тебя есть всегда и везде.

tailgunner ★★★★★
()
Ответ на: комментарий от anonymous

Черт, я дерево)

...TreeMap...

А почему не просто HashMap? Оно же, вроде, стандартный контейнер для такого.

Тогда бы можно было сразу использовать find_or_insert, что бы лишний раз не дергать find.

... get ... get_mut ...

Эх, вот бы добавили параметризацию по мутабельности :( .

ozkriff
()
Последнее исправление: ozkriff (всего исправлений: 1)
Ответ на: комментарий от anonymous

В Rust разве индексация не приведёт к вставке объекта, если его не было?

От конкретного контейнера зависит же, а не от языка.

Чего в нем странного? Ожидаемое и очень удобное поведение.

Мне кажется, что должны быть два разных метода, потому что они, как минимум, должны отличаться мутабельностью. Собственно, так у HashMap и сделано: find_or_insert (&mut self) и find (&self).

ozkriff
()
Последнее исправление: ozkriff (всего исправлений: 1)
Ответ на: комментарий от tailgunner

иначе - только агрегация

Отстой

Делегирование у тебя есть всегда и везде

Имеется в виду ручное делегирование? Нет, спасибо...

anonymous
()
Ответ на: комментарий от tailgunner

Юзай Скалу

scala хороша, да. Но вот C++ тоже нужен иногда... А тут вроде как замена(по крайней мере лучший кандидат на нее).

anonymous
()
Ответ на: комментарий от ozkriff

А почему не просто HashMap? Оно же, вроде, стандартный контейнер для такого.

Не заметил.

Эх, вот бы добавили параметризацию по мутабельности :( .

И опцию для отключения «split-stack». Или я опять чего-то упустил? :)

anonymous
()
Ответ на: комментарий от anonymous

вот бы добавили параметризацию по мутабельности :( .

И опцию для отключения «split-stack». Или я опять чего-то упустил? :)

Да, кое-чего упустил. Split-стеков давно уже нет.

tailgunner ★★★★★
()
Ответ на: комментарий от tailgunner
$ rustc --version
rustc 0.12.0-pre
attributes #0 = { uwtable "split-stack" }
attributes #1 = { "split-stack" }
attributes #2 = { inlinehint uwtable "split-stack" }
attributes #3 = { nounwind "split-stack" }
	.type	_ZN4main20hbe4f658f944e57a0eaaE,@function
_ZN4main20hbe4f658f944e57a0eaaE:
	.cfi_startproc
	cmpq	%fs:112, %rsp
	ja	.LBB0_0
	movabsq	$152, %r10
	movabsq	$0, %r11
	callq	__morestack
	retq
.LBB0_0:
anonymous
()
Ответ на: комментарий от tailgunner

Вооружившись лупой, нашел только атрибут «no_split_stack».

anonymous
()
Ответ на: комментарий от peregrine

Допустим, вот что пишут в документации по экспериментальной реализации модулей для С++ в секции «Problems with the current model» парни из clang (думаю, их в неосиляторстве плюсов обвинить сложно):

The #include mechanism provided by the C preprocessor is a very poor way to access the API of a library, for a number of reasons:

  • Compile-time scalability: Each time a header is included, the compiler must preprocess and parse the text in that header and every header it includes, transitively. This process must be repeated for every translation unit in the application, which involves a huge amount of redundant work. In a project with N translation units and M headers included in each translation unit, the compiler is performing M x N work even though most of the M headers are shared among multiple translation units. C++ is particularly bad, because the compilation model for templates forces a huge amount of code into headers.
  • Fragility: #include directives are treated as textual inclusion by the preprocessor, and are therefore subject to any active macro definitions at the time of inclusion. If any of the active macro definitions happens to collide with a name in the library, it can break the library API or cause compilation failures in the library header itself. For an extreme example, #define std «The C++ Standard» and then include a standard library header: the result is a horrific cascade of failures in the C++ Standard Library’s implementation. More subtle real-world problems occur when the headers for two different libraries interact due to macro collisions, and users are forced to reorder #include directives or introduce #undef directives to break the (unintended) dependency.
  • Conventional workarounds: C programmers have adopted a number of conventions to work around the fragility of the C preprocessor model. Include guards, for example, are required for the vast majority of headers to ensure that multiple inclusion doesn’t break the compile. Macro names are written with LONG_PREFIXED_UPPERCASE_IDENTIFIERS to avoid collisions, and some library/framework developers even use __underscored names in headers to avoid collisions with “normal” names that (by convention) shouldn’t even be macros. These conventions are a barrier to entry for developers coming from non-C languages, are boilerplate for more experienced developers, and make our headers far uglier than they should be.
  • Tool confusion: In a C-based language, it is hard to build tools that work well with software libraries, because the boundaries of the libraries are not clear. Which headers belong to a particular library, and in what order should those headers be included to guarantee that they compile correctly? Are the headers C, C++, Objective-C++, or one of the variants of these languages? What declarations in those headers are actually meant to be part of the API, and what declarations are present only because they had to be written as part of the header file?

http://clang.llvm.org/docs/Modules.html

Странно, что необходимость синхронизировать информацию между .cpp и .h не упомянули.

ozkriff
()
Последнее исправление: ozkriff (всего исправлений: 1)
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.