LINUX.ORG.RU

модульность в rust

 


1

2

Скажите как в расте скормить компилятору несколько библиотек. Я пишу модуль, например mud.rs компилю его с ключем "--crate-type lib". Получаю libmod.rlib. Затем пускаю собирать другой модуль с ключем "-L путь-до-спапки-с-первым-модулем". Но компилер все равно ругается на импорт «use mod::*». Что я делаю не так?

★★★★★

http://doc.rust-lang.org/reference.html#extern-crate-declarations

http://doc.rust-lang.org/reference.html#use-declarations

Note: Unlike in many languages, use declarations in Rust do not declare linkage dependency with external crates. Rather, extern crate declarations declare linkage dependencies.

Тебе нужен extern crate имямодуля;.

use чего-то-там; только управляет видимостью имен.

И, если не точно знаешь что делаешь, лучше не дергать самому rustc руками, а использовать cargo.

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

Разобрался. Компилеру нужно явно прописывать "--extern имя-модуля=файл.rlib". В карго тоже надо как-то прописывать такие вещи руками. Сам он не хочет разруливать мне зависимости и собирать модули. Как ему это указать я не знаю.

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

Решил проблему с карго прописав каждому модулю в Cargo.toml

[lib]
name="имя-модуля"
path="src/файл.rs"
Если честно, я думал, что карго сам может искать их и решать зависимости, как например ocamlbuild или ghc.

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

Разобрался. Компилеру нужно явно прописывать "--extern имя-модуля=файл.rlib".

Эм, можно просто в коде написать extern crate имя_библиотеки; и при вызове rustc указать директорию с собранной библиотекой - rustc main.rs -L path/to/libs

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

Вот же в руководстве пример подключения библиотеки color: http://doc.crates.io/guide.html#adding-dependencies

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

Для подключения внешних библиотек в Cargo.toml твоей программы надо приписать, например (из руководства):

[dependencies.color]
git = "https://github.com/bjz/color-rs.git"

Тогда cargo выкачает ее в ~/.cargo/..., соберет и потом прилинкует к твоей программе.

А [lib] нужен, если библиотека является частью твоего пакета, как, например, в cgmath-rs (где весь пакет состоит только из библиотеки).

Если честно, я думал, что карго сам может искать их и решать зависимости, как например ocamlbuild или ghc.

Для этого нужна база данных пакетов, ее пока нет. Если не ошибаюсь, это попозже хотят сделать.

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

Я немного не понимаю, что ты там вообще делаешь)) Можешь чуть подробней (возможно, со файловой структурой) описать, что ты пытаешься сделать?

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

Да просто хотел в качестве упражнения для изучения раста. Написать на нем простенький lisp интерпретатор. В папке src/ несколько модулей. Никаких внешних зависимостей. А по поводу extern: с этой директивой все собирается.

[lib]
name="lex"
path="src/lex.rs"
без нее ошибки
/Users/algiz/code/tests.rust/parser/src/main.rs:2:1: 2:18 error: can't find crate for `lex`
/Users/algiz/code/tests.rust/parser/src/main.rs:2 extern crate lex;
                                                         ^~~~~~~~~~~~~~~~~
error: aborting due to previous error
Could not compile `parser`.

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

А зачем тебе для этого либы? Используй обычные модули.

src/main.rs:

mod lexer;
mod parser;

// ...

Структура исходников:

λ desktop rust → tree src
src
├── lexer.rs
├── parser
│   └── mod.rs
└── sample.rs

1 directory, 3 files

Rule of thumb: если в исходнике встречается mod MODNAME;, то компилятор ищет его содержимое в файле MODNAME.rs либо в MODNAME/mod.rs.

theNamelessOne ★★★★★
()
Последнее исправление: theNamelessOne (всего исправлений: 2)
Ответ на: комментарий от Aswed

Если тебе просто надо разнести на несколько модулей, то так:

$ tree
.
├── Cargo.toml
└── src
    ├── main.rs
    └── test.rs

1 directory, 3 files
$ cat src/main.rs
mod test;

fn main() {
    test::test_func(1);
}
$ cat src/test.rs 
pub fn test_func(n: i32) {
    println!("test: {}", n);
}
$ cat Cargo.toml 

[package]

name = "test_mod"
version = "0.0.1"
authors = [
   "ozkriff@gmail.com",
]

[[bin]]
name = "test_mod"
$ cargo run -v
   Compiling test_mod v0.0.1 (file:///home/ozkriff/Downloads/lor-rust-mod)
     Running `rustc /home/ozkriff/Downloads/lor-rust-mod/src/main.rs --crate-name test_mod --crate-type bin -g --out-dir /home/ozkriff/Downloads/lor-rust-mod/target --dep-info /home/ozkriff/Downloads/lor-rust-mod/target/.fingerprint/test_mod-452e11bfcdee8e92/dep-bin-test_mod -L /home/ozkriff/Downloads/lor-rust-mod/target -L /home/ozkriff/Downloads/lor-rust-mod/target/deps`
     Running `target/test_mod`
test: 1
$ cargo clean
$ cargo run
   Compiling test_mod v0.0.1 (file:///home/ozkriff/Downloads/lor-rust-mod)
     Running `target/test_mod`
test: 1
$ ./target/test_mod 
test: 1
$ 

Так это все соберется как одна еденица трансляции.

Просто модули в Cargo.toml указывать не надо, их найдет rustc.

Посмотри http://rustbyexample.com/mod.html. В смысле не только эту страничку, но и 12.1, 12.2, 12.3 и 12.4 (особенно последний, там как раз на разные файлы разносят).

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

Это то, что я и хотел узнать. Спасибо.

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

Спасибо за такой полный ответ.

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

Мега вопрос. В карго можно изменить структуру проекта? В смысле, не хочу использовать корневую src директорию. Допустим, я хочу назвать её иначе. Как-то это указать можно?

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

Аа, ты об этом. Я опечатку не заметил.

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

Есть опциональное поле path.

Спасибо.

Но звучит как плохая идея.

Почему? По моему, наоборот: прибивать гвоздями «срц» - плохая идея. Не все проекты следуют таким соглашениям. Достаточно проектов, над которыми работал, организовывались иначе: директории с названиями компонентов в корне проекта. Тесты, кстати, тоже часто лежат внутри компонентов. Многие билд системы разрешают это конфигурировать - не вижу в этом ничего плохого.

Кстати, ещё один вопрос. Почему в Cargo.toml указывается "[lib]", но "[[bin]]"?

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

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

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

Типы традиционно начинаются с заглавной буквы, поэтому к ним применён CamelCase. Кроме того, типы встречаются куда реже, чем прочий код.

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

Типы традиционно начинаются с заглавной буквы, поэтому к ним применён CamelCase.

А кто мешает Писать_так? Ну если мы принимаем аргумент про важность подчёркиваний для читабельности.

Кроме того, типы встречаются куда реже, чем прочий код.

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

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

Кстати, ещё один вопрос. Почему в Cargo.toml указывается "[lib]", но "[[bin]]"?

Наверное, поэтому:

At the moment, Cargo only supports a single lib per package. We use the array format for future extensibility.

We only plan to support a single lib at the moment because if you have multiple libs, you would want projects to be able to depend on them separately. If you don't care about that, why do you have separate libs?

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

А кто мешает Писать_так?

Наверное, привычка. В куче языков принято для именования типов использовать UpperCamelCase, я даже перечислять их не буду.

Ну если мы принимаем аргумент про важность подчёркиваний для читабельности.

Важность подчеркивания для читабельности проявляется только для функций и переменных, потому что они пишутся в snake_case, т. е. в нижнем регистре; без подчеркиваний нельзя было бы определить отдельные слова в составном индикаторе. Для CamelCase такая проблема не стоит.

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

А кто мешает Писать_так?

Уродливо же. Кроме того, в современных языках и ML'ах в частности такое не принято.

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

Важность подчеркивания для читабельности проявляется только для функций и переменных, потому что они пишутся в snake_case

А, ну и для глобальных констант в SCREAMING_SNAKE_CASE, по той же причине.

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

А, ну и для глобальных констант в SCREAMING_SNAKE_CASE, по той же причине.

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

Важность подчеркивания для читабельности проявляется только для функций и переменных, потому что они пишутся в snake_case,

Это какое-то рекурсивное объяснение. Можно же было использовать тот же кемел кейс, но без заглавной первой буквы.

В куче языков принято для именования типов использовать UpperCamelCase, я даже перечислять их не буду.

Да это всё понятно. Просто я часто сталкивался с тем, что или всё с подчёркиваниями или кемел кейс, где типы с большой, а функции и переменные с маленькой. К последнему и привык, потому и спрашиваю. Впрочем, погуглил и вижу, что «растовский» стиль достаточно распространён.

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

Как мне кажется, выбор между lowerCamelCase и snake_case скорее всего определяется вкусами разработчиков, чем какими-нибудь объективными причинами. Я вообще такими вопросами не заморачиваюсь, а просто пишу в том стиле, в котором написана стандартная библиотека языка.

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

а просто пишу в том стиле, в котором написана стандартная библиотека языка.

Я в общем-то тоже легко приспосабливаюсь к принятому стилю, при необходимости. Тем не менее предпочтения всё равно выработаться успели и если требований нет, то пишу «как нравится». В расте, с одной стороны, предлагаемый стиль не особо нравится. С другой - каждый раз отключать предупреждения не кажется хорошей идеей.

Неоднородность стиля со стандартной библиотекой пугает меньше. Возможно, потому что y С++ одного стиля и так нет.

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

Про именование и [lib]/[[bin]] уже ответили.

Про «почему плохая идея» - потому что cargo основан на определенных соглашениях, а не на возможности настраивать все и вся для каждого проекта индивидуально. Он предоставляет способы обойти эти соглашения, если очень надо, но это уже костыли и не будет так удобно.

Не все проекты следуют таким соглашениям.

Судя по rust-ci.org, почти все проекты на ржавчине следуют)

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

Посмотри на организацию servo - https://github.com/servo/servo/blob/a25874626/Cargo.toml.

...
[dependencies.net]
path = "components/net"

[dependencies.msg]
path = "components/msg"

[dependencies.util]
path = "components/util"

[dependencies.script]
path = "components/script"
...

Компоненты лежат в директории components (никто не помешает в корень положить) в виде отдельных пакетов (личные тесты пакетов - внутри пакетов, само собой). Вроде, довольно близко к описанному тобой и не противоречит идеалогии cargo.

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

потому что cargo основан на определенных соглашениях

Тогда плохо, что всё прибито гвоздями, а не гибко настраивается.

Судя по rust-ci.org, почти все проекты на ржавчине следуют)

Это плохой аргумент. Понятное дело, что следуют, если нормально работает только один способ. Плюс разработчики пилят под себя и им, очевидно, так удобно. Остальные - энтузиасты, которым не принципиально.

Но я-то про проекты «вообще». Смысл ломать привычки потенциальных «пользователей» в таком непринципиальном вопросе?

Посмотри на организацию servo

Посмотрел. Теперь мне ещё менее понятно зачем им такая организация.

Мне как-то больше нравится несколько иная организация. И часто именно с такой и сталкивался:

project
├── component_a
│   └── resources
│   └── tests
│   └── str_1
│   └── str_2
│   └── ...
├── component_b
│   └── resources
│   └── tests
│   └── str_1
│   └── str_2
│   └── ...

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

Кстати, ещё один момент не совсем понимаю. В гайде говорится следующее:

Either the trait or the type you're writing the impl for must be inside your crate.

Это что ж получается - я не смогу не смогу добавить трейт из одной либы к структуре из другой? Какое-то странное ограничение. Или я чего-то не понимаю?

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

Это плохой аргумент.

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

Тогда плохо, что всё прибито гвоздями, а не гибко настраивается.

Ну как плохо, подход просто другой. Я не большой фанат языка Go, много с чем в нем не согласен и для моих целей он подходит очень слабо, но я нахожу его довольно интересным и мне по душе стремление разработчиков к простоте и их подход к вопросу «соглашения против конфигурирования».

Смысл ломать привычки потенциальных «пользователей» в таком непринципиальном вопросе?
Мне как-то больше нравится несколько иная организация.

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

Кстати, насколько я понимаю, через какое-то время собираются сделать нормальный rustfmt, когда синтаксис совсем устаканится и определятся с правилами в Rust Guidelines.

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

Я не большой фанат языка Go,

По моему, Go просто ужасно убог. Так что апелляции к этому языки для меня выглядят неубедительно. (:

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

Ничего не имею против выбранного подхода по умолчанию. Но запрещать (нормально) настраивать - всё-таки перебор. Тем более, что в описании Карго говорится, что планируется и сборка проектов на других языках. Плюс расширения/возможность настроить во многих местах закладывается вполне сознательно.

и стандартную программу, вроде gofmt, что бы автоматически следить за этим.

Мне больше нравится инструменты типа clang форматера. Где можно задавать свои правила. При этим, по умолчанию, можно иметь «стандартный» набор.

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

Плюс иногда предметная область диктует свои термины. Удобно когда в спеке и в коде вещи называются одинаково. Жесткие правила тут будут мешать.

DarkEld3r ★★★★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.