LINUX.ORG.RU

Rust и типобезопасность

 ,


3

7

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

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

Почитав документацию:

The as keyword does safe casting

Набросал такой примерчик:

fn main() {
        let a: f64 = std::f64::MAX;  // данное значение просто пример "большого числа"
        let b = a as i64;
        println!("{}", b);
}

Вопросы:

1) С какой стати это вообще компилируется?

2) Да, f64 и i64 нужно одно и то же количество битов для хранения значения, ну и что?

3) Почему результат меняет знак?

Представьте, что какой-то тех. процесс идет, и определенный параметр нельзя изменять скачкообразно, иначе физически система (по крайней мере, один из компонентов) выйдет из строя (встанет в раскоряку, взорвется, выпустит токсичный газ, убивающий 100тыс. населения; нужное подчеркнуть). И вот значение в f64 положительное и увеличивается постепенно, с i64 все вроде в порядке (и тестирование проходит на тестовых значениях), и вдруг хренак! и уже -9223372036854775808

Как так?

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

У вас очень короткая память.

Нет, у тебя:

Rust и типобезопасность (комментарий)

А можно скачать Qt для MSVC и пытаться использовать его из mingw, но ничего не выйдет.

Это и с С не сработает, проблема языков у которых больше одной реализации,

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

А чем для Rust https://crates.io/crates/abi_stable не подходит?

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

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

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

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

И это не совсем троллинг. Есть проекты на C++ до сих пор в продакшн и генерирующие мега прибыль, которые о >=C++11 ничего не слышали, STL игнорируют полностью (слышу, как революционеры одного хелловорлда кричат с галерки: «это не C++!!!», но кого из цивильных людей волнует их мнение…). И те весьма немногочисленные классы на шаблонах, которые таки используются, можно без особого труда переписать на конкретные явные специализации.

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

Есть проекты на C++ до сих пор в продакшн и генерирующие мега прибыль, которые о >=C++11 ничего не слышали

Есть такие проекты и на С, и на FORTRAN и на PL/1. Да и многие холиварщики втихую пишут себе на JS, C++, C# и т.п., а на форумы ходят рассказать про прелести Rust, CommonLisp, Erlang и прочего разного интересного.

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

Есть такие проекты и на С, и на FORTRAN и на PL/1.

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

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

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

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

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

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

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

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

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

Так может пора отвязаться от LLVM и написать уже Rust на Rust по взрослому?

Нет, шашечки не нужн

Нужны и пишутся: https://github.com/bytecodealliance/wasmtime/tree/master/cranelift

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

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

Нет.

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

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

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

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

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

Ну это по сути тоже самое, только другими словами. Большинство фич в попсовых языках добавляют только для программистов (внезапно), чтоб им было сухо и комфортно. Вот взять какой-нибудь Kotlin - ну вкусно же, вкусно, и начать писать на нем можно просто сразу, без особой подготовки. И джависты тоже в восторге. Но можно ли представить, чтоб в нем сделали управление памятью как в Rust? Очевидно нет, такие «фичи» нужны только тем, кто ставит удобство и скорость разработки ниже чем эффективность и корректность. А это далеко не все.

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

масштабные проекты не особо задумываются о «времени жизни переменных внутри одного треда».

Неверно. Это в утилите, которая запускается, делает 2-3 операции и выходит можно забивать на время жизни, можно тупо выделять память и не освобождать, да даже если освободить и потом обратиться, вероятно, ничего страшного не произойдёт. А в масштабных проектах, особенно в многопоточке, очень важно следить за владением доступом и т.п.

для языка масштабных проектов важно описание взаимодействия процессов, способы декомпозиции, инкапсуляции, обеспечения общей устойчивости и проч…

И всё это реализует Раст как минимум не хуже плюсов, именно за счёт семантики заимствования.

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

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

в русте нет нормального опп. а приколы со временем жизни переменных - вешь мало кому нужная.

Трудно написать что-то более неверное. Наоборот, это ООП в 99% нахрен не нужно, это идиотская идея из 80х, которая уже к концу 80х показала кучу присущих ей проблем и жила только на хайпе. Всё что есть в ООП хорошего, а именно инкапсуляция, полиморфизм (статический и динамический) есть в Расте. Есть ограниченное наследование интерфейсов. Чего нет, так это наследования реализации, но тут такая проблема: любая вменяемая книга по ООП, выпущенная в 90х и позже всячески советует наследование реализации избегать.

никто не видит чужих кишок, и не занимается анализом времен жизни чужих кишок в своем контексте.

И это тоже неверно. Кишки периодически выдаются, почти любая библиотека/фреймворк на C/C++ имеют функции для получения/освобождения разных объектов, даже когда получаемое и освобождаемое представляет собой просто буферы памяти. Аналогично на приём, очень много лишней работы происходит просто от того, что в современных языках нет возможности сообщить о том, что данный объект отдаётся навсегда.

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

Наверное поэтому автор Раста ушел писать Swift.

Он молод и ему нужны деньги. Кстати, на форониксе была новость о том, что Эпол ищет раст-программистов. Так что им одного Хоара мало.

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

Почему бы тогда не придумать, что автор либы просто возьмёт и сломает, ну чисто по приколу?

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

а раст требует постоянной пересборки всего и вся.

В раст прямо сейчас работает карга, которая раздаёт либы и весьма успешно. Видимо АБИ для этого не нужен.

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

В раст прямо сейчас работает карга, которая раздаёт либы и весьма успешно. Видимо АБИ для этого не нужен.

Вот есть Qt 4.4.3 https://download.qt.io/archive/qt/4.4/qt-win-opensource-4.4.3-mingw.exe

Он собран gcc 4… И исходники не соберутся без правок на gcc 9.

Но можно свою программу на Qt слинковать с этим Qt 4.4.3 и программа будет работать…

А в Rust можно использовать какую-нибудь либу времён Rust 0.10 на которую автор забил, и которая не соберётся текущим растом? Насколько я понимаю нет, в этом и ценность ABI.

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

Кстати, на форониксе была новость о том, что Эпол ищет раст-программистов

Сюда ее ес-но уже притаскивали, если вкратце - даже сиплюсников Эпол ищет на несколько порядков больше, ну и ес-но речь не шла про использование Rust в macOS/iOS.

В раст прямо сейчас работает карга, которая раздаёт либы и весьма успешно. Видимо АБИ для этого не нужен.

Опенсорсные либы. Я только за опенсорс, но, очевидно, что многие конторы не будут использовать Rust по этой причине. Разве что для внутренних каких-нибудь решений и/или полноценных приложений, но ес-но не завязанных на проприетарные SDK.

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

Есть ограниченное наследование интерфейсов. Чего нет, так это наследования реализации, но тут такая проблема: любая вменяемая книга по ООП, выпущенная в 90х и позже всячески советует наследование реализации избегать.

вот вопрос. как это написать на расте.

using cstring = const std::string;

class Named{
	cstring _name;
public:
	Named(cstring &fname):_name(fname){}
	cstring &name()const{return _name;}
};

class Foo:public Named{
	Foo(cstring &fname):Named(fname){}
};

void fff(){
	Foo lfoo("foo");
	std::cout<<lfoo.name();
}
alysnix ★★★
()
Последнее исправление: alysnix (всего исправлений: 1)
Ответ на: комментарий от alysnix

Это просто. Сложнее перестать думать в терминах классов, наследующих данные и реализации методов.

trait Named {
    fn name(&self) -> &str;
}

struct Foo {
    name: String,
}

impl Foo {
    fn new<T>(name: T) -> Foo
    where
        T: Into<String>,
    {
        Foo { name: name.into() }
    }
}

impl Named for Foo {
    fn name(&self) -> &str {
        &self.name
    }
}

fn main() {
    let foo = Foo::new("foo");
    println!("{}", foo.name());
}
red75prim ★★★
()
Ответ на: комментарий от red75prim

Это просто. Сложнее перестать думать в терминах классов, наследующих данные и реализации методов.

это совершенно не одно и то же. приведенный вами код был бы аналогом плюсовому, если б я там написал абстрактный метод name(),и заставил всех наследников его реализовывать.

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

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

В общем, в вопросе извлечения информации из тел функций, я так понимаю, что Царь слился.

Насчёт вывода типов: я бы посоветовал Царю прекратить переписывать словарь под свои хотелки. Vtable - это vtable, интерфейс - это интерфейс. У термина «вывод типа» есть устоявшееся значение, и оно не имеет никакого отношения к трансформации типа. Компилятор смог назначить тип без явного указания - значит вывод типа есть. И у Раста вывод типов гораздо мощнее чем у C++, C#, Java, я это продемонстрировал на нормальном коде. Это чудило сначало начало нести какую-то хрень, типа я использовал некую библиотеку, а в самом расте вывода типов нет. Это бред, библиотекой такие фичи не реализуются. Да и не подключал я никаких библиотек, тут вектор из стандартной либы и трейт From для полуявной конвертации оттуда же.

use core::fmt::Debug;
use std::vec::Vec;
use std::convert::From;

fn foo<T:Debug>(bar: &T) {
    println!("bar == {:?}", &bar);
}

//Для начала, скорее всего все мои объяснения бесполезны, потому как вероятно клоун просто перепастил какую-то херню. - Нет, но это абсолютно нерелевантно, написал, перепастил, один хрен пример демонстрирует то, что демонстрирует. Так что Царь хлебнул говна в прямом эфире.

fn main() {
    let mut v1 = Vec::new(); // Нигде ничего не выводится выводится.
//- Нет. Тут выводится тип переменной v1. Более того, в отличие от слабого вывода типов в языках типа плюсов или шарпа, тут просто офигенный момент: никакого Vec::new() в природе не существует. Существует дженерик Vec<T> и в нём метод new(), который, внезапно, уникален для каждого инстанса Vec<T>. Так что в этой строке выбирается правильный T для Vec<T>, и выбирается из контекста функции. Если убрать все v1.push и v1.extend, то компилятор станет ругаться, потому что не сможет догадаться, что это был за T в Vec<T>::new()
    let mut v2 = Vec::new(); // Ошибка клоуна объясняется следующим.
    // Клоун сидит в пхп и ему рассказали, что v1/v2 - это какие-то переменные, либо ещё какая херня
    // В реальности же нет. В этом недоязычке это так не работает и v1/v2 - это просто алиасы на выражение.
// - Чушь. Если бы v1 был алиасом к выражению Vec::new(), то каждое v1 в тексте превращалось бы в вызов функции создания вектора. Если считать, что v1 - это алиас не к выражению, а к памяти, в которой хранится результат выражения, то мы получаем обычную переменную.
    
    // второе свойство данной скриптухи. Т.к. здесь всё vtable(если угодно сектантам "динамические интерфейсы"),
// -нет, никакой динамики, тут всё статическое.
    
    // Далее, если вы внимательно читали мои объяснения - вы знаете, что в недоязычке неважен тип T. Важны именно связи.
// - нет, в "недоязычке" тип T важен, он известен, "недоязычёк" ругается, если ему подсунуть не то.
    // Т.е. путь проброса типа.
    
    // И вот где-то дальше происходит утверждение типа, руками. Допустим клоун вызывает extend(T: Vec<int>)
    // И на это недоязычку насрать - его не интересуют типы.
    
    // И далее происходит вызов, допустим, extend(T: vec<float>). Это единственный инвариант, который поддерживает данный мусор.
    
    // Алгоритм следующий. Необходимо указать тип руками, всегда. Тип - это vtable, т..е динамический интерфейс.
    // Далее типизация никого не интересует. Получается Vec -> Vec -> Vec<int> -> Vec<float> - нужно понимать, что тип цепочки всегда Vec, но цепочка не может содержать разные типы.

я оставил этот бред просто чтоб продемонстрировать какую Царь несёт хрень. Дальше вырежу и поступлю проще, переведу слова Царя на человеческий. Идея Царя заключается в том, что фронтенд языка Раст не выводит тип, а просто пихает что дают пока это не попадёт «украденному» LLVM, который в процессе кодогенерации и начнёт ругаться. Даже если бы это было правдой, это никак бы не отменило наличие вывода типов и тайпчека в расте, детали устройства компилятора совершенно неважны в обсуждении свойств языка, занимается ли этим в rustc фронтенд или бэкенд, неважно.

Однако, предположение Царя неверно, можно убедиться довольно просто:

use core::fmt::Debug;
use std::vec::Vec;

fn foo<T1: Debug + Copy, T2: Debug + Copy>(i1: T1, i2: T2) {
    let mut v1 = Vec::new();
    let mut v2 = Vec::new(); 
    // let v3 = Vec::new(); // ошибка, компилятор не может понять тип v3 из контекста
    let v3 = Vec::<bool>::new(); // а так норм, явно указан.
    v1.extend(
        (1..=5).map(
            |_|{
                v2.push(i2); // автовывод типа v2  это Vec<T2>. Автоавывод из побочного эффекта замыкания!
                i1 // автовывод возвращаемого значения T1 и, соответственно v1 Vec<T1>
            })
    );
    // v1.push(v2[0]); // Ошибка. Типы элементов не совпадают
    println!("v1 == {:?}, v2 == {:?}, v3 == {:?} ", &v1, &v2, &v3)
}

fn main(){
    foo(1,2); // T1 == T2 == i32, однако это не спасёт от ошибки если раскоментировать v1.push(v2[0])
    // Что доказывает, что вывод типов производится и без обмана  и халявы и проверка типов осуществляется не только до начала кодогенерации, но и до инстанциорования.
    // Так что Царь снова хлебнул в прямом эфире.
}

// Тут клоун несёт какую-то херню про ворованный llvm-ir. Это ничего не значит.

Царюшка, llvm-ir - это то, что фронтенд rustc скармливает LLVM для последующей обработки. Если на этом уровне foo::() и foo::() представлены как 2 независимые функции (а это так, ты можешь убедиться), значит ты попал пальцем в небо, когда предположил, будто бы инстанциация производится LLVM на этапе кодогенерации. Почему тебе такие простые вещи нужно разжёвывать?

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

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

А чем программу-то собрать? Девятым гцц? И как оно линковаться будет, если в пятом гцц поменяли аби?

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

А чем программу-то собрать? Девятым гцц? И как оно линковаться будет, если в пятом гцц поменяли аби?

Да, девятым. Но оно сейчас не работает, я проверил :( Так что сейчас патчить исходники Qt 4.4.3 и собирать новым компилятором…

undefined reference to `__gxx_personality_sj0'
undefined reference to `_Unwind_SjLj_Register'
undefined reference to `_Unwind_SjLj_Resume'
undefined reference to `_Unwind_SjLj_Unregister'

Но раньше работало, я собирал так программы точно после gcc5. Qt не использует STL, поэтому что поломали STL abi это не важно было. А вот то что сейчас в mingw32 сменили тип исключений с sjlj на DWARF оказалось важным…

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

И у Раста вывод типов гораздо мощнее чем у C++, C#, Java, я это продемонстрировал на нормальном коде

Ты это продемонстрировал на каноничном и чуть ли не единственном примере, который везде предлагают. И сравнивать так некорректно, так разные языки имеют разные ограничения и возможности. К примеру, в Rust нельзя написать аналог:

auto join = [](auto pref) {
    return array { [=](auto... v) { 
        return (... + (pref + lexical_cast<decltype(pref)>(v)));
    } };
};
    
cout << join("   "s).front()(1, 2., "3", "4"s);
cout << join(0.).front()(1, 2.);

Нет перегрузки, шаблонов, параметров значений, дефолтных аргументов и много другого. Легко уметь ограниченный набор возможностей и им манипулировать, но как только набор возможностей начинает расширятся - вылазят пачки проблем, а сложность резко возрастает, как c той же специализацией. Потому я бы сказал, что вывод типов есть везде, но каждый язык использует его с учетом своих остальных особенностей.

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

Серьёзный вопрос. Какие типы тут выводятся? Только тип результата +? Выводится, что он должен быть равен типу pref или что-то более хитрое?

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

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

Какие типы тут выводятся? Только тип результата +?

Вообще-то все, тут нет ни одного явно указанного типа. Кроме разве что array, но и у того параметр выводится.

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

Нет, они выводятся на их основе.

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

Всё что есть в ООП хорошего, а именно инкапсуляция, полиморфизм (статический и динамический) есть в Расте.

«ООП» взлетело не из-за этого, а из-за выразительности, коей нет в расте, где предлагается писать месиво из struct/impl/trait/self/::new_*, в котором все разбросано по частям. А так то ООП можно на чем угодно изобразить, да и много что назвать им.

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

В Расте для статического полиморфизма сделаны генерики, для динамического — трейты. Наверное, видит, как и все языки, имеющие эти фичи. Но не ясно, зачем вы человека сравниваете с ЯП.

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

За такой код нужно сажать.

Это еще ладно, за одну только сигнатуру такой функции на Rust пришлось бы расстреливать.

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

В примере статический полиморфизм. Никаких абстрактных методов и vtable там нет.

быть не может. в растовом коде - Named это просто интерфейс, который предлагается реализовать всем, кто хочет быть совместимым с Named. если б там было 2(или 22) класса, что хотели бы быть совсместимыми с Named, им, судя по всему, предлаглось бы реализовать независимо свойство name(), каким-то способом. и это совершенно не то, что я написал в плюсовом примере.

В русте предлагается исполнить контракт на класс - реализовать свойство name():&string. а в плюсовом случае, наследникам дается готовая реализация, общая для всех. сколько у вас будет в русте в бинарнике экземпляров функций name(), что пришлось реализовать в N классах, что выполнили контракт на функцию name()?

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

В плюсах уже осилили implicit return? const auto постоянно писать тоже не задалбывает?

implicit return я считаю ненужным и даже вредным, а вот иммутабельности по дефолту не хватает, согласен.

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

В плюсах уже осилили implicit return?

пора бы расту осилить уже implicit self. :) а то писанина селфа в сигнатуре функции внутри трейта не радует. или там может быть описана статическая функция, которой не нужен селф?

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

сколько у вас будет в русте в бинарнике экземпляров функций name(), что пришлось реализовать в N классах, что выполнили контракт на функцию name()?

N и будет:

(вот тут две на 18 и 23 строчке в asm)

https://gcc.godbolt.org/z/xEjsUf

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

N и будет

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

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

в моем же случае жестко фиксирована реализация хранения базового свойства

И это запрещено в грамотном ООП.

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

и в коде она присутсвует в одном экземпляре, а не в десятках

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

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

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

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

К примеру, в Rust нельзя написать аналог:

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

Ладно, попробую дописать, как понимаю, тут у нас шаблон в массиве сидит и с разными типами инстанциируется. Как это вообще работает? Попробуем:

#include <iostream>
#include <array>
#include <string>
#include <boost/lexical_cast.hpp>
#include <cstdlib>
using namespace std::literals::string_literals;
 
int main()
{
    auto join = [](auto pref) {
        return std::array { [=](auto... v) { 
            return (... + (pref + boost::lexical_cast<decltype(pref)>(v)));
        } };
    };
    
// а теперь пытаемся воспользоваться
    if (rand()&1) {
        join = [](auto postf) {
            return std::array { [=](auto... v) { 
                return (... + (boost::lexical_cast<decltype(postf)>(v) + postf));
            } };
        };

    }


std::cout << join("xxx"s).front()(1, 2., "3", "4"s)<< std::endl;
std::cout << join(0.).front()(1, 2.);
}

Опаньки. Плюсы оказались языком, который не выполняет свои обещания. Чё толку мне сохранять темплейт в переменную, если воспользоваться этим нельзя?

Нет перегрузки, Внезапно: есть. Например см трейт From.

/*функция возвращает f64 */
if(smth) return From::from(1);
return From::from(10f32);

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

Это ограниченная перегрузка, но её предлагается использовать там, где это действительно нужно, а не просто тупо плодить 100500 функций с одинаковым именем.

Перегрузка в плюсах - это ошибка, которая дорого стоит. Страуструп конкретно лоханулся с конструкторами, их не должно было быть. Из-за невозможности различать их по именам, пришлось добавлять перегрузку и получать кучу проблем, когда вызов с интом указывает размер создаваемого объекта, а с контейнером - инициализирует объект содержимым. Добавив сюда параметры по умолчанию, получили полную жопу и теперь вынуждены писать имена параметров Something(capacity: 100) чем это лучше чем растовый Something::with_capacity(100) - непонятно.

шаблонов,

Шаблоны - говно. Идея «копипастнем вот это, попробуем откомпилять, не получилось - копипастим следующий вариант» она как бы даже выраженная словами выглядит идиотски. Ни один последующий язык не стал добавлять шаблоны, все делают нормальные дженерики. Кстати, читал недавно предложение запилить в плюсы нормальную кодогенерацию, чтоб можно было библиотекой qobject реализовать, без вызова внешней moc, или interface, чтоб в абстрактный базовый класс компилялся, plaindata или даже обычный плюсовый struct, типа видит компилятор такое слово и вызывает библиотечный кодогенератор, который из объявления сделает обычный класс, добавив public в нужных местах.

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

параметров значений

Это да, недоработка. Но некритичная.

дефолтных аргументов

а вот это создаёт больше проблем чем решает.

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

Нельзя так делать ручками прописывается вызов публичных методов внутреннего объекта. Тогда у вас будет грамотная инкапсуляция, а не спагетти-код.

Ох уж теоретики, почитатели книг, паттеранов и антипаттернов. Более корявого, переусложненного, тормозного и нерабочего кода чем у них трудно найти. Зато никто не расскажет больше них о «правильном» коде. Композиция действительно бывает лучше наследования, но бывает и хуже, о чем, кстати, в действительно хороших книгах и статьях должны упоминать.

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

Он собран gcc 4… И исходники не соберутся без правок на gcc 9.

Плюсопроблемы не беспокоят белых людей. Вообще, как это не соберётся, плюсы же обратносовместимые.

Но можно свою программу на Qt слинковать с этим Qt 4.4.3 и программа будет работать…

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

А в Rust можно использовать какую-нибудь либу времён Rust 0.10

Шикарно. А чего не написал времён 1.0? Всё-таки альфа языка… ну как бы несерьёзно.

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

Ох уж теоретики, почитатели книг, паттеранов и антипаттернов. Более корявого, переусложненного, тормозного и нерабочего кода чем у них трудно найти. Зато никто не расскажет больше них о «правильном» коде. Композиция действительно бывает лучше наследования, но бывает и хуже, о чем, кстати, в действительно хороших книгах и статьях должны упоминать.

Ну как бы определись, нужен ООП или нет. Если нужен - делай нормально. Если не нужен - не делай. А так получается типичная отмазка говнокодера: «я читал в по-настоящему умной книге, что наследование иногда лучше, поэтому я всегда тупо наследуюсь чтоб «пропатчить» 1 метод, и топчись оно всё конём».

Поэтому, кстати, никакое ГСЛ плюсы не спасёт. Плюсовики будут послушно прописывать лайфтаймы, где всё тривиально и прописывать не нужно, а там где нужно 5 минут подумать, чтоб сделать лайфтайм-безопасный интерфейс, тупо забьют. А если старший спросит - с умным видом расскажут, что вообще лайфтаймы - говно, с ними нельзя двусвязный список реализовать. В умной книге читал, да.

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

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

плюсовизм головного мозга довольно странно работает. В Расте вывод типа объективно лучше

Попробую переключиться на твой стиль общения. Фанатизм головного мозга мешает понять, что в Rust нет глобального вывода типов? Что в нем тех же контекстов для вывода типов? Что ты сравниваешь теплое и мягкое?

Опаньки. Плюсы оказались языком, который не выполняет свои обещания. Чё толку мне сохранять темплейт в переменную, если воспользоваться этим нельзя?

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

… куча фанатского бреда про то, что или «есть» в Rust или ненужно

Тут и отвечать не на что, любой вменяемый человек понимает что это чушь.

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

Ну как бы определись, нужен ООП или нет. Если нужен - делай нормально. Если не нужен - не делай.

Вот только «нормально» у всех разное. У кого-то ООП это CLOS, а ваш Rust это жалкий обрубок цэпэпэ, у кого-то это Smalltalk (все никак времени не найду на него), у кого-то это JS с его динамичностью, у кого-то Python с его гибкостью, у кого-то это Qt, у кого-то COM и т.д. А нормальный ООП это абстракция мало имеющая отношения к реальности.

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

Попробую переключиться на твой стиль общения. Фанатизм головного мозга мешает понять, что в Rust нет глобального вывода типов?

Глобального вывода типов и в плюсах нет, так-то.

Я уже выше раз 3 объяснил, почему вывод типа за пределы тела функции (кроме замыкания) вреден. Четвёртый раз нужно объяснять?

В расте два «одинаковых» замыкания тоже имеют разный тип.

Давай ты не будешь дурачка включать. ТЫ, лично ты обещал возможность сохранить шаблон в переменную. Очевидно, что такая штука на статически компилируемом языке нормально работать не будет. Я и показал, что она не работает.

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

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

Тут и отвечать не на что, любой вменяемый человек понимает что это чушь.

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

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

Вот только «нормально» у всех разное.

Ты дурачка-то не включай. Нормальное ООП начинается с инкапсуляции. Если ты инкапсуляцию ломаешь, чтоб пропатчить метод, то ООП у тебя отсутствует по определению.

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

ТЫ, лично ты обещал возможность сохранить шаблон в переменную

Дай ссылку на это и я признаю, что не прав во всем, и что Rust идеален.

Я уже выше раз 3 объяснил, почему вывод типа за пределы тела функции (кроме замыкания) вреден. Четвёртый раз нужно объяснять? Слив засчитан

Про то, что если чего-то нет в любимом языке фанатика, то оно кривое и вредное, и так все давно знают. Можно заранее все сливы засчитывать.

шаблоны, которые после плюсов никто не стал реализовывать

Я бы посоветовал бы чуть расширить свой кругозор.

потому что всем понятно, что это тупик

Всем понятно. Ясно.

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

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

Named::name() вызывается через vtable только для трейт-объектов.

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

Собственно я уже ответил. Подход к структурированию кода с помощью иерархий классов - не единственный. В коллекции объектов строки с именами объектов можно положить отдельно других данных объектов, и такая коллекция всё-равно сможет выдать интерфейс Named для индивидуальных объектов. Вот и будет одна реализация.

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