Здравствуйте, sergii.p, Вы писали:
SP>не понимаю какого ООП вам там не хватает? Трейты — фактически абстрактные классы. Указатели есть. Пример на С++ который трудно/невозможно написать на Rust в студию.
Ну вот пример:
С++
| | Скрытый текст |
| | #include <iostream>
#include <memory>
class Base1 {
public:
virtual ~Base1() = default;
virtual void fun1() const {
std::cout << "fun1=" << a << std::endl;
}
virtual void fun2() const {
std::cout << "fun2=" << a << std::endl;
}
virtual void fun3() const {
std::cout << "fun3=" << a << std::endl;
}
virtual void fun4() const {
std::cout << "fun4=" << a << std::endl;
}
private:
int a = 0;
};
class V1 : public Base1 {
public:
void fun1() const override {
std::cout << "_fun1=" << a << std::endl;
}
private:
int a = 1;
};
class V2 : public V1 {
public:
void fun2() const override {
std::cout << "__fun2=" << a << std::endl;
}
private:
int a = 2;
};
class V3 final : public V2 {
public:
void fun3() const override {
std::cout << "___fun2=" << a << std::endl;
}
private:
int a = 3;
};
int main() {
const auto v3 = std::make_unique<V3>();
v3->fun1();
v3->fun2();
v3->fun3();
v3->fun4();
return 0;
}
|
| | |
Т.е. в наследнике вы трогаете только то что отличается. Все просто 3 класса, поочередно наследуются. Это может быть как UI-виджеты так и некая система событий.
А вот Rust аналог авто-генеренный
Куча сущностей вместо трех простых записанных, намноОООго сложнее для восприятия. Т.е. теряется простота. А в последнем V3 вам еще нужно помнить что было переписано в V2 — это на малом количестве методов — а если их будут сотни?
В общем — слишком сложный для восприятия ООП-код получается. Не пригодно для промышленного использования.
| | Скрытый текст |
| | trait Base1Ops {
// доступ к "Base1::a"
fn base1_a(&self) -> i32;
// виртуальные методы (есть дефолт, как в Base1)
fn fun1(&self) {
println!("fun1={}", self.base1_a());
}
fn fun2(&self) {
println!("fun2={}", self.base1_a());
}
fn fun3(&self) {
println!("fun3={}", self.base1_a());
}
fn fun4(&self) {
println!("fun4={}", self.base1_a());
}
}
struct Base1 {
a: i32,
}
impl Base1 {
fn new() -> Self {
Self { a: 0 }
}
}
impl Base1Ops for Base1 {
fn base1_a(&self) -> i32 {
self.a
}
}
struct V1 {
base: Base1,
a: i32, // V1::a
}
impl V1 {
fn new() -> Self {
Self {
base: Base1::new(),
a: 1,
}
}
}
impl Base1Ops for V1 {
fn base1_a(&self) -> i32 {
self.base.a
}
fn fun1(&self) {
println!("_fun1={}", self.a);
}
}
struct V2 {
v1: V1,
a: i32, // V2::a
}
impl V2 {
fn new() -> Self {
Self { v1: V1::new(), a: 2 }
}
}
impl Base1Ops for V2 {
fn base1_a(&self) -> i32 {
self.v1.base.a
}
// наследуем поведение V1::fun1
fn fun1(&self) {
self.v1.fun1();
}
fn fun2(&self) {
println!("__fun2={}", self.a);
}
}
struct V3 {
v2: V2,
a: i32, // V3::a
}
impl V3 {
fn new() -> Self {
Self { v2: V2::new(), a: 3 }
}
}
impl Base1Ops for V3 {
fn base1_a(&self) -> i32 {
self.v2.v1.base.a
}
// наследуем поведение V1::fun1 и V2::fun2
fn fun1(&self) {
self.v2.fun1();
}
fn fun2(&self) {
self.v2.fun2();
}
fn fun3(&self) {
// в исходнике строка "___fun2=" (хотя это fun3) — сохраняю как есть
println!("___fun2={}", self.a);
}
// fun4 не переопределяем => будет Base1-версия через дефолт
}
fn main() {
let v3 = Box::new(V3::new());
v3.fun1();
v3.fun2();
v3.fun3();
v3.fun4();
}
|
| | |