LINUX.ORG.RU

Rust, несколько вопросов.


0

2

Возникло несколько вопросов, возможно, кто-то сможет подсказать.

1.

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

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

2. Если у меня есть файл «test.rs», то содержимое окажется в модуле «test». Можно ли как-то изменить имя модуля не переименовывая файл?

tailgunner ozkriff theNamelessOne

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

...в третьей.

Почему в третьей? Ведь в «приложении» (в смысле не либе) тоже не смогу?

Ну и про коллизии понятно. Но разве необходимости явно обьявлять use не достаточно?

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

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

Почему в третьей? Ведь в «приложении» (в смысле не либе) тоже не смогу?

Не сможешь.

Ну и про коллизии понятно. Но разве необходимости явно обьявлять use не достаточно?

Но у трейта одно имя.

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

Но у трейта одно имя.

Можно же явно неймспейс указывать? Как-то так:

impl std::Clone for X {
...

impl custom::Clone for X {
...

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

Но у трейта одно имя.

Можно же явно неймспейс указывать?

Это ничего не меняет. Трейт один, и полностью квалифицированное имя у него одно. А то, что ты написал (если так можно) реализует два разных трейта.

Про модуль не знаю, но тоже читал, что можно.

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

А по второму вопросу подскажешь что-то? Даже в гайде написано:

Rust does not impose a particular relationship between your filesystem structure and your module structure. That said, there is a conventional approach to how Rust looks for modules on the file system, but it's also overridable.

Но вот как это изменить?

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

и полностью квалифицированное имя у него одно.

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

Тем более, что оно «почти работает».

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

и полностью квалифицированное имя у него одно.

И что? Вернее, не так. Почему оно обязано быть одно?

Если оно не одно, то и проблемы нет.

Тем более, что оно «почти работает».

У тебя определено 2 разных трейта.

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

У тебя определено 2 разных трейта.

Не совсем понимаю. Хочешь сказать, что из-за неймспейсов они разные?

Ну ок, но как это заставить работать? Если добавлю use b::MegaTrait, то будет неоднозначность:

<anon>:33:20: 33:28 error: multiple applicable methods in scope [E0034]
<anon>:33     println!("{}", s.test());
...
<anon>:26:5: 28:6 note: candidate #1 is `S.b::MegaTrait::test`
...
<anon>:20:5: 22:6 note: candidate #2 is `S.a::MegaTrait::test`
Но полный путь, кажется, задать нельзя:
<anon>:33:25: 33:34 error: expected `<`, found `MegaTrait`
<anon>:33     println!("{}", s.a::MegaTrait::test());

В любом случае, если у меня два разных трейта, то как они могут оказаться одинаковыми при использовании двух разных библиотек с разными неймспейсами?

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

Не совсем понимаю. Хочешь сказать, что из-за неймспейсов они разные?

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

если у меня два разных трейта, то как они могут оказаться одинаковыми при использовании двух разных библиотек с разными неймспейсами?

Если отвечать на вопрос буквально - «никак не могут».

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

Если отвечать на вопрос буквально - «никак не могут».

Я тебя очень часто не понимаю. Иногда даже кажется, что ты троллишь.

Вот смотри, меня интересовало почему нельзя «у себя» (в отдельном crate) реализовывать трейты из одной либы для типов из другой. Ты аргументировал это тем же, чем и туториал - могут быть коллизии. Ок, мысль понятна. Хотя идея «просто запрещать» всегда мне казалась убогой - можно ведь не компилировать с неоднозначностью, благо она детектится легко (так ведь?) и заставлять разруливать конфликты вручную.

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

Возникает вопрос - где я туплю и в чём не прав? Или же всё-таки этот запрет смысла не имеет и реально проблем не возникнет?

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

Я тебя очень часто не понимаю. Иногда даже кажется, что ты троллишь.

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

можно ведь не компилировать с неоднозначностью, благо она детектится легко (так ведь?)

На этапе компиляции? Вообще не детектится. Представь, что на одном краю земли Алиса сделала крейт foo с трейтом foo::FooTrait; на другом Боб сделал крейт bar с типом bar::Bar; на третьем краю Чарли реализовал foo::FooTrait для bar::Bar в крейте baz; на четвертом краю Дэвид реализовал foo::FooTrait для bar::Bar в крейте bletch. Теперь крейты baz и bletch встретились в одной программе, которая пытается использовать методы FooTrait на объекте типа Bar. Мы имеем конфликт между крейтами baz и bletch, поскольку они оба несут реализацию foo::FooTrait для bar::Bar. Как здесь поможет _компилятор_? Линковщик - может быть, но компилятор? Конфликтующие крейты изготовили Чарли и Дэвид, которые не знали друг о друге. Как подобный конфликт обходит предлагаемое тобой решение?

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

Примерно то же самое мне кажется сейчас.

«Я не специально». Правда. Так что объяснения буду рад услышать.

Как здесь поможет _компилятор_? Линковщик - может быть, но компилятор?

Возможно, ты прав. Честно говоря, просто не задумывался в какой момент станет понятно, что у нас несколько реализаций трейта. В любом случае, мне не особо нравится С++ подход, когда многие проблемы о которых линковщик мог бы сообщать «игнорируются» (в смысле UB, но обычно всё работает).

Но разве когда мы в программе пишем:

extern crate baz;
extern crate bletch;
Tо компилятор не может увидеть, что у нас имена символов совпадают?

Как подобный конфликт обходит предлагаемое тобой решение?

Например, так:

extern crate baz;
extern crate bletch;

// Переименовываем.
use baz::foo::FooTrait as BazFooTrait;
use bletch::foo::FooTrait as BletchFooTrait;

// Используем функцию foo из разных реализаций трейта FooTrait.
b.BazFooTrait::foo();
b.BletchFooTrait::foo();
Не очень изящно, но такие ситуации не должны часто возникать. Тем не менее, вероятность такого далеко не нулевая. Ну серьёзно - вот у нас сейчас в проекте сериализация из одной либы успешно работает с типами из другой. Обе либы не наши.

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

Но разве когда мы в программе пишем:

extern crate baz;
extern crate bletch;

Tо компилятор не может увидеть, что у нас имена символов совпадают?

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

Например, так:

extern crate baz;
extern crate bletch;

// Переименовываем.
use baz::foo::FooTrait as BazFooTrait;
use bletch::foo::FooTrait as BletchFooTrait;

Кхм. Нет никакого baz::foo::FooTrait (baz::FooTrait тоже нет); в baz есть некая реализация foo::FooTrait для bar::Bar, у этой реализации даже имени нет (?).

Кроме того, допустим, у тебя есть крейт bzz, который использует baz, и boo, который использует bletch; ты используешь оба крейта; чтобы жизнь малиной не казалась, оба крейта без исходников. Какую реализацию foo::FooTrait для bar::Bar будет использовать каждый крейт?

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

Если автор bar сам не позаботился о реализации FooTrait для bar::Bar, то newtype MyBar, impl FooTrait for MyBar, и у тебя есть юзабельная реализация FooTrait. Да, ты не сможешь передать bar::Bar туда, где требуется foo::FooTrait, но селяви. В Си++ ты бы создал класс, производный от foo::FooTrait, и содержащий (указатель на) bar::Bar, с той же проблемой, не?

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

Или при запуске используется более новая версия крейта, чем при компиляции?

Ну а если в «более новой версии» автор повыкидывал используемые функции?

Кстати, динамической загрузки «пока», вроде, нет?

у этой реализации даже имени нет (?).

Дык, имя могло бы быть?

В Си++ ты бы создал класс, производный от foo::FooTrait, и содержащий (указатель на) bar::Bar, с той же проблемой, не?

В С++ есть множественное наследование...

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

Или при запуске используется более новая версия крейта, чем при компиляции?

Ну а если в «более новой версии» автор повыкидывал используемые функции?

Речь, естественно, идет о строгом расширении функционала. Когда такое расширение ломает совместимость - это нельзя оправдать.

Дык, имя могло бы быть?

Причем оно могло бы быть еще и pub, ага. Не слишком много хлопот для решения такой проблемы?

В Си++ ты бы создал класс, производный от foo::FooTrait, и содержащий (указатель на) bar::Bar, с той же проблемой, не?

В С++ есть множественное наследование...

И как оно поможет, если ты хочешь обработать через FooTrait объект Bar, который вернула Bar *bar::foo()?

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

Когда такое расширение ломает совместимость - это нельзя оправдать.

Так, стоп. А с чего оно поломает, если реализацию трейтов всё равно надо явно «импортировать»? То есть загрузилась более новая версия, где такая реализация есть. Но мы-то о ней не знаем - соответственно и не используем, dlsym для неё не вызываем.

Не слишком много хлопот для решения такой проблемы?

...нет?

И как оно поможет, если ты хочешь обработать через FooTrait объект Bar, который вернула Bar *bar::foo()?

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

Кстати, Карго с гитом/меркуриалом работать умеет просто для того, чтобы в игнор нужные файлы прописывать? Я не про выкачивание зависимостей из гита, а именно про создание проекта с параметрами "--git"/"--hg".

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

Когда такое расширение ломает совместимость - это нельзя оправдать.

Так, стоп. А с чего оно поломает, если реализацию трейтов всё равно надо явно «импортировать»?

Эээ... речь идет о гипотетическом расширении Rust или о текущем состоянии языка?

Не слишком много хлопот для решения такой проблемы?

...нет?

...да? Никаких неразрешимых проблем это не вызывает. И кажется, что это можно решить в backwards-compatible manner.

если ты хочешь обработать через FooTrait объект Bar, который вернула Bar *bar::foo()?

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

Насколько я понимаю, в Rust всё то же самое.

Карго

Я пока общаюсь с Rust только в playpen, так что ничего не могу сказать о Cargo.

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

Эээ... речь идет о гипотетическом расширении Rust или о текущем состоянии языка?

Дык, трейты и сейчас надо импортировать? Если разрешить разные реализации, то явный импорт следует оставить. Или я не так понял?

И кажется, что это можно решить в backwards-compatible manner.

Сейчас не актуально. И лучше решать нормально, чтобы потом легче было.

Насколько я понимаю, в Rust всё то же самое.

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

DarkEld3r ★★★★★
() автор топика

2. Если у меня есть файл «test.rs», то содержимое окажется в модуле «test». Можно ли как-то изменить имя модуля не переименовывая файл?

А зачем, собственно? Можно через use-объявления указать другое имя, может ты про это?

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

Rebinding the target name as a new local name, using the syntax use p::q::r as x;.

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

А зачем, собственно?

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

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

src
├── main.rs
├── utils
│   └── mod.rs
│   └── common.rs
│   └── misc.rs
То есть в «common.rs» и «misc.rs» реализуем, что надо, а «mod.rs» нужен просто, чтобы это был модуль.

Может, конечно, это я после С++ «хочу странного», но фраза из гайда меня обнадёжила, что смогу получить желаемое.

Можно через use-объявления указать другое имя, может ты про это?

Нет. Так придётся каждый раз по месту использования это прописывать. А хотелось бы наоборот сделать использование более удобным.

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

Можно не размазывать.

Ты издеваешься? (:

Я в курсе, что можно хоть 100500 модулей в одном файле делать. Но очевидно же, что это пример. Если модули обьёмные, то пихать в один файл может быть не слишком удобно. Хотелось бы как раз наоборот - в разных файлах, но без приседаний с промежуточным mod.rs.

Кстати, ленивый вопрос (наверное, сам найти смогу, но вдруг ты знаешь). Как реализованы вещи типа std::io::stdio::stderr? В плане модулей/файлов.

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

Аноним про переэкпорт уже успел написать.

Нет. Так придётся каждый раз по месту использования это прописывать. А хотелось бы наоборот сделать использование более удобным.

Я это и имел ввиду - ты можешь в модуле не просто сделать pub use self::common::*;, но и при этом переименовать модуль, используя pub use ... as ...;. Т.е. у тебя в недрах библиотеки может быть файл с одним названием, но его содержимое в интерфейсе библиотеки будет вообще с другим названием экспортироваться.

С размазыванием модуля по файлам тоже не очень удобно получается, надо городить такие костыли

Ну хз, насколько это неудобно. Может я просто привык уже к ржавчине и мне тяжело взглянуть со стороны, но мне кажется очень логичным. А как ты хотел бы что бы это делалось? Что бы, если нет mod.rs, то все файлы из папки считались бы частями модуля?

Допустим, в питоне __init__.py тоже для модулей обязан быть (он может быть пустым, но файл-то должен быть), вроде никто не сходит с ума от неудобства.

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

Кстати, ленивый вопрос (наверное, сам найти смогу, но вдруг ты знаешь). Как реализованы вещи типа std::io::stdio::stderr? ...

Очень ленивый, это и правда десять секунд: http://doc.rust-lang.org/std/index.html?search=stderr -> http://doc.rust-lang.org/std/io/stdio/fn.stderr.html и там есть ссылка src.

... В плане модулей/файлов.

Так и реализованы, вроде ничего особенного.

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

Очень ленивый,

...я не увидел там кнопку "[src]".

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

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

А про трейты что думаешь? Ты же, вроде, что-то более-менее «реальное» на расте пишешь? Или тоже не так страшно как кажется со стороны?

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

А про трейты что думаешь? ...

Вот статейка по этому поводу, которую получилось нагуглить. Она 2012ого года, но, вроде, в разрезе этого разовора актуальна:

http://smallcultfollowing.com/babysteps/blog/2012/10/04/refining-traits-slash...

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

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

... Ты же, вроде, что-то более-менее «реальное» на расте пишешь? ...

Я потихоньку пошаговую стратегию ковыряю, чего-то вроде Battle Academy 2 хочу в итоге получить. Тут вот старая заготовка валяется - https://github.com/ozkriff/marauder, я его обновляю под ночные сборки ржавчины (вдруг кому как какой-никакой пример пригодится). Сейчас разбираюсь, как это дело на андроид перенести.

... Или тоже не так страшно как кажется со стороны?

У меня пока не возникало необходимости в серелизации структур из чужих крейтов или чем-то подобном, так что точно сказать не могу. Но необходимость делать типы-обертки мне не кажется чем-то катастрофическим. В конце концов, у меня в том же коде Мародера почти нет обычного i32 - везде стараюсь обертки использовать - UnitId, MeshId, GridIndex, и т.д. (не зря же на языке со строгой типизацией пишу, надо пользоваться проверкой типов) - ничего, вроде с обертками жить вполне можно.

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

Вот статейка по этому поводу, которую получилось нагуглить.

Спасибо, почитаю.

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

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

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