LINUX.ORG.RU

Наследование в Rust

 , ,


1

8

Здравствуйте. Потихоньку изучаю Rust. Насколько я понял, пробежавшись по документации, в Rust ООП реализовано через struct и trait. Это так? Так вот, как реализовать наследование? Например есть следующий код:

struct Shape {
    x: f64,
    y: f64,
}

trait Circle: Shape {
    
}

trait Square: Shape {
    
}

Как мне в Circle после наследования от Shape создать поле radius? То есть что то типа

trait Circle: Shape {
    radius: f64,
}

Вообще было бы здорово, если бы кто-нибудь рассказал про ООП в Rust. Заранее спасибо большое :)

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

Алгебраически типы, pattern matching, вывод типов - всё это взято из Си.

Насколько я понимаю, типы выводятся не всегда и не везде

Да.

а это несколько противоречит тому, что принято в функциональных языках

Глобальный вывод типов не обязателен для функционального языка.

Да и императивен Rust насквозь.

Никто не утверждал обратного. Rust - это гибрид Си и ML.

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

O

Тут проблемы, поскольку без наследования это делается только руками(реализация интерфейса + агрегирование).

Проблема, о которой ты говоришь (нежелательный тривиальный бойлерплейт код в ситуации, когда ты почти полностью наследуешь реализацию), к open/closed principle никакого отношения не имеет.

L

В rust справедливо лишь в отношении интерфейсной части

Liskov substitution справедливо по отношению ко всему, что можно делать средствами Rust. В чем проблема?

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

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

И насколько «простой» должна быть эта возможность?

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

Всем пофиг на синтаксис, кроме анона.

Ну да, почему ж тогда ни один из языков с затейливым синтаксисом не взлетел?

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

Ну да, почему ж тогда ни один из языков с затейливым синтаксисом не взлетел?

Достаточно того, что семейство си-подобных языков с {}-синтаксисом безоговорочно доминирует в отрасли. На этом можно перестать дрочить вокруг выбора базового синтаксиса и заняться чем-нибудь более продуктивным.

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

Всем пофиг на синтаксис

Не верю.

Синтаксис не должен быть ужасен (APL, Perl) или тотально непривычен (Lisp), остальное неважно. Синтаксис Rust не хуже, чем у Си/Objective-C/C++/Swift.

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

Достаточно того, что семейство си-подобных языков с {}-синтаксисом безоговорочно доминирует в отрасли

Нет. Раст хоть и C-подобен, но слишком замусорен и пересахарен. Уже скала на этом навернулась, никому не нужна ведь. А сколько было энтузиазма ещё не так давно.

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

Раст хоть и C-подобен, но слишком замусорен и пересахарен. Уже скала на этом навернулась, никому не нужна ведь.

Scala, если и «навернулась», то не из-за синтаксиса. Rust, если не взлетит, тоже не из-за синтаксиса.

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

Ага, еще, например, неплохо бы иметь модули/классы, открытые для расширения и закрытые для модификации...

Это вполне может быть частью трейта.

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

не из-за синтаксиса

В том числе. Вот не получается втиснуть концепции из функциональщины в С-одежонку, хоть тресни. И у раста не получится. Язык только-только вылупился, а уже на него обрушились тонны критики (справедливой) за нечитаемость.

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

Вот не получается втиснуть концепции из функциональщины в С-одежонку, хоть тресни

double_facepalm.jpg

тонны критики (справедливой) за нечитаемость

Давай Топ5 этой критики.

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

Rust - это гибрид Си и ML

Это невозможно с точки зрения семантики. В функциональных языках нет состояния, нет вычислителя. Есть лишь вычисления. Если, конечно, мы не берём в руки ref. Но это явно выделенная часть языка, в отличие от.

С точки зрения императива, можно совместить функциональщину и императив, а с точки зрения функциональщины - нельзя =)

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

Rust - это гибрид Си и ML

Это невозможно с точки зрения семантики

Ну окей.

С точки зрения императива, можно совместить функциональщину и императив

Продолжай неукоснительно следовать взаимоисключающим параграфам.

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

С точки зрения императива, можно совместить функциональщину и императив, а с точки зрения функциональщины - нельзя =)

Наслаждайся. «OCaml offers a happy compromise here, making it easy and natural to program in a pure style, but also providing great support for imperative programming. This chapter will walk you through OCaml's imperative features, and help you use them to their fullest.» https://realworldocaml.org/v1/en/html/imperative-programming-1.html

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

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

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

Речь была о том, что на функциональщине императив выразить можно, но это бессмысленно. А вот на императиве реализовать слой псевдо-функциональщины можно.

но это бессмысленно.

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

Ну так он гибридный, да.

Давай-ка составим топ10 не-гибридных. Я начну:

1. Хаскель.

Теперь с тебя еще 9 популярных функциональных языков, в которых нет императивщины. Давай, просвети меня.

Чего сказать-то хотел?

Хотел сказать, что практической альтернативы гибридным языкам не существует. В этом расписались авторы окамла, лиспа и многих других «функциональных» языков; в этом расписались авторы sicp. С другой стороны баррикад, даже в дремучем c++ изначально предусмотрены first-class functions: называются они функторами, и для них есть отличный сахар под названием boost::bind/boost::function.

Речь была о том, что на функциональщине императив выразить можно, но это бессмысленно.

Так ocaml — это бессмысленный язык? Ну, как скажешь.

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

В Java почему-то кодеры могут исключения.

Не факт. Просто в Java объекты обычно долго не живут, кривое состояние особо не повредит, т.к. на каждый запрос все объекты пересоздаются. Конкретно exception-safe код в Java пишут очень редко. Тут скорее уместен вопрос — насколько эта концепция в принципе критична.

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

Хаскель настолько же гибридный, насколько Окамл.

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

Ну да, почему ж тогда ни один из языков с затейливым синтаксисом не взлетел?

Python, Ruby, PLSQL, R?

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

Так ocaml — это бессмысленный язык? Ну, как скажешь.

В ocaml императивщина не выражена через функциональщину.

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

А какие варианты? Я особо не следил, но есть вот и вот разные варианты. Правда, судя по дате, оба документа не сильно новые.

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

Так что когда исключение на самом деле вылетает, программа приходит в неконсистентное логическое состояние

На кодах возврата может быть точно такая же фигня.

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

обрушились тонны критики (справедливой) за нечитаемость.

В основном, вижу «критику» только на лоре. Да и то она вся ограничивается «ой как ужасно, хуже чем в С++/перле/...». Чем хуже никто внятно ответить не может.

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

Отсутствие исключений уже существенный минус. Это все еще стандарт по обработке ошибок в отрасли. В Си их нет, да. Но это скорее проблема, чем преимущество, хотя и оправдано отсутствием платы в рантайме. Но у rust'а рантайм все-равно на уровне крестов, как я понимаю. Или я ошибаюсь?

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

Дженерики слабее шаблонов, а на макросах код писать сложнее, чем на шаблонах. А если кресты таки обзаведутся концептами, текущее преимущество rust по указания ограничений испарится.

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

Хотя по факту мне не хватает опыта работы с Rust...

Но тут какое дело... Когда я после проекта на крестах, работаю с Python, например, или Scala, то это практически отдых на фоне C++. Когда же я пытаюсь использовать Rust, то... Мне даже хочется вернуться на кресты. Это как-то нездорово. Неприятно, когда приходится себя заставлять что-то изучать(не совсем верное слово, новых концепций в Rust для меня нет). Пока надеюсь, что дело во мне.

Опять же, на работе имею возможность использовать Rust и навязать его другим, но совесть не позволяет. Пока даже сам себе не могу доказать хоть какую-то пользу от него. Удивительно, но для Go задачу можно спокойно найти и аргументировано обосновать его применения...

forCe
()
Последнее исправление: forCe (всего исправлений: 1)

Классического ООП в Rust нет. Но есть trait'ы, позволяющие описывать «интерфейсы»(в широком смысле этого слова), а так же указывать реализацию по умолчанию, есть impl'ы, позволяющие указывать методы типа и то, как он реализует тот или иной trait. Наследование реализации отсутствует, есть только агрегация. (авто)Делегирования реализации trait'а полю или какого-то другого способа «унаследовать»/использовать реализацию так же нет.

Примеры в этой теме есть.

Жить с этим можно, но не хочется так просто.

А какими языками владеете?

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

Спасибо Вам огромное за подробное разъяснение чего да как. Насчет исключений... Есть Result и try!, с помощью которых можно организовать обработку ошибок. Не исключения конечно, но хоть что то. А из языков на 100 % никаким не владею. Только изучаю все это. Со школьных лет pascal/delphi, немного C и так по мелочи.

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

черезжопное там ООП=) В общем то все понятно, но кажется много лишнего, чтобы сделать элементарное. Интересно, насколько на практике подход Rust лучше, чем классический ООП. Ну насчет exception'ов все понятно. Их нет:D Но вот borrowing и move semantics это вообще ад.По крайней мере для меня - новичка.

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

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

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

Ну, допустим, не хватает HKT и do-нотации. Из-за первого приходится реализовывать все комбинаторы для каждого типа заново (очень интересно), из-за второго - пользоваться костылями вроде try!.

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

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

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

Ну, допустим, не хватает HKT и do-нотации.

Первое, вроде, хотят реализовать.

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

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

Отсутствие исключений уже существенный минус.

А какое отношение это имеет к синтаксису/нечитаемости? Я так понял, что претензия была именно к нему.

А вообще, растовая паника - практически исключения. Ведь деструкторы вызываются, передавать можно любой тип, а не только строки, да и перехватить можно, правда только на границе потоков. Так что я не удивлюсь, если потом и «нормальные исключения» поверх этого прикрутят. Более того - недавно появилась функция catch_panic, которая примерно это и делает. Осталось разве что упростить вытаскивание типа «исключения» и всё.

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

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

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

Дженерики слабее шаблонов

Нет. Они как раз мощнее.

а на макросах код писать сложнее, чем на шаблонах

Опять нет. Для написания простых вещей (например, контейнеров и алгоритмов) растовые макросы как раз не нужны - достаточно дженериков. А если мы возьмём что-то «более сложное», вроде, метапрограммирования в бусте, то растовые макросы уже совсем не сложнее получаются.

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

Вообще-то, так и происходит.

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

Можно немного подробнее?

Вкусовщину комментировать не буду. Мне вот с растом разбираться интересно.

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

Жить с этим можно, но не хочется так просто.

Ну знаешь - в расте даже нет функций с переменным количеством аргументов. Или дефолтных значений для аргументов. В итоге проблема решается макросами (vec!) или размножением функций: new, with_capacity и т.д. Но это говорит не о том, что «всё пропало», а о том, что язык, по сути, ещё развивается. Да, нам обещали обратную совместимость после 1.0, но все эти вещи, в том числе и наследование, обсуждаются и их, почти наверняка, добавят.

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

В общем то все понятно, но кажется много лишнего, чтобы сделать элементарное.

Например?

Но вот borrowing и move semantics это вообще ад.

И что там страшного? Первое обьясняется парой простых правил. Второе тоже ничего сложного не несёт.

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

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

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

Осталось разве что упростить вытаскивание типа «исключения» и всё.

Ага, дело за малым.

В плюсах есть хедеры и цпп файлы - тоже своего рода разделение.

Но это как раз здравое разделение, соответствует интерфейсу и имплементации одной и той же сущности. А в Rust ты имеешь разные сущности. Понимаешь разницу?

По моему, это всё как раз говорит о том, что разделять интерфейс/реализацию удобно.

Удобно разделять интерфейс и реализацию сущности. И иметь набор чистых интерфейсов. А в расте ты заводишь отдельную сущность для интерфейса, отдельную сущность для структуры данных, отдельную сущность для реализации операций, отдельные сущности для реализации интерфейсов. Это неудобно.

Нет. Они как раз мощнее.

А аргументация будет? Я тоже считаю шаблоны мощнее дженериков. Особенно с появлением концептов. Для языка уровня C и C++ важно сочетать обобщенный код, генерацию кода и специализацию кода.

Опять нет. Для написания простых вещей (например, контейнеров и алгоритмов) растовые макросы как раз не нужны - достаточно дженериков.

А если я хочу специализацию? А если я хочу переменное число аргументов дженерика? А если я хочу переменное число аргументов функции?

Вообще-то, так и происходит.

Если бы так и происходило, то в интерфейсной части стандартной библиотеки не было бы столько ада с '.

Мне вот с растом разбираться интересно.

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

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

Ну знаешь - в расте даже нет функций с переменным количеством аргументов

И это минус.

Или дефолтных значений для аргументов.

И это минус.

В итоге проблема решается макросами (vec!)

Из пушки по воробьям.

или размножением функций: new, with_capacity и т.д.

Костыли вы мои, костыли...

их, почти наверняка, добавят.

Посмотрим.

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

Например?

Вместо того, чтобы просто сказать: «Есть фигура. У фигуры можно узнать позицию. Есть круг. Круг - это фигура. У круга можно спросить радиус», мы говорим: «Есть фигура. У нее можно узнать позицию. Есть данные фигуры. Данные фигуры содержат позицию. Данные фигуры позволяют узнать позицию. Есть круг. Круг должен быть фигурой. У круга можно узнать радиус. Есть данные круга. Данные круга содержат данные фигуры и радиуса. Данные круга позволяют узнать позицию. Данные круга позволяют узнать радиус». Жесть. На ровном месте.

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

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

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

Может, оно и почти тоже самое, только выглядит более костыльно.

Давай конкретно чем. Только с учётом, что сейчас оно, разумеется, не тянет на полноценные исключения. В остальном же, чем panic! отличается от throw? Сделают макросами привычный catch и разницы вообще не будет.

Ага, дело за малым.

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

Понимаешь разницу?

Да, но не вижу в этом проблемы. Опять же, ты можешь добавлять реализацию трейтов для сторонних типов. А «стандартное ООП» такого не позволяет. Если на всякие extension methods посмотреть, то там получается примерно так же.

А аргументация будет?

Думал, что общеизвестно. В плюсах у шаблонов, по сути, «утиная типизация» (до концептов ещё дожить надо). Можно передавать что угодно и если для переданного типа есть подходящие функции/методы, то всё сработает. В расте дженерики вполне «типизированные» - мы явно указываем ограничения на тип.

Изначально я тоже думал, что плюсовый подход удобнее, но потом мнение поменял. Опять же, в расте вариант С++ можно изобразить на макросах, но сомневаюсь, что это будет частый случай.

А если я хочу специализацию? А если я хочу переменное число аргументов дженерика? А если я хочу переменное число аргументов функции?

Опять же, ничего этого ПОКА нет. А вообще специализацию для разных типов написать можно, но сейчас компилятор не умеет выбирать правильный вариант. Я не вижу принципиальных ограничений для реализации. Готов спорить, что скоро оно будет.

Переменное количество аргументов пока не поддерживается. Меня оно тоже напрягает. Вроде, есть предложения на эту тему.

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

И это минус.

Естественно. А разве кто-то доказывает ненужность этих вещей?

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

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

Жесть. На ровном месте.

Наследования пока нет, ага. А вот с «ужасностью» разделения реализации и данных не согласен. Немного непривычно, только и всего.

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

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

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

просто всем насрать на других ресурсах на Rust

Да ладно?

И ты сюда зашёл специально чтобы сказать насколько тебе насрать?

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