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 ★★★★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.