Возможно ли, чтобы одна структура расширила существующую структуру, сохранив все поля?
Используя rust 1.2.0
Проблема
Я все еще изучаю Rust (исходя из фона Javascript) и пытаюсь выяснить, возможно ли для одной структуры StructB
расширить существующую структуру StructA
, чтобы StructB
все поля, определенные на StructA
.
В Javascript (синтаксис ES6) я мог бы по существу сделать что-то вроде этого...
class Person {
constructor (gender, age) {
this.gender = gender;
this.age = age;
}
}
class Child extends Person {
constructor (name, gender, age) {
super(gender, age);
this.name = name;
}
}
Ограничения
-
StructA
- это внешний cargo
пакет, который я не контролирую.
Текущий прогресс
Я нашел этот пост в блоге по одинарному наследованию, который звучит как именно то, что мне нужно.
Но попытка его реализации привела к появлению этого сообщения об ошибке error: virtual structs have been removed from the language
. Некоторые поиски позже, и я узнал, что он был реализован, а затем удален в RFC-341 довольно быстро.
Также найден этот поток об использовании признаков, но так как StructA
из внешнего пакета груза, я не думаю, что это возможно для меня превратить в черту.
Итак, каков был бы правильный способ сделать это в Rust?
Ответы
Ответ 1
Нет ничего, что бы точно соответствовало этому. Есть два понятия, которые приходят на ум.
-
Структурная композиция
struct Person {
age: u8,
}
struct Child {
person: Person,
has_toy: bool,
}
impl Person {
fn new(age: u8) -> Self {
Person { age: age }
}
fn age(&self) -> u8 {
self.age
}
}
impl Child {
fn new(age: u8, has_toy: bool) -> Self {
Child { person: Person::new(age), has_toy: has_toy }
}
fn age(&self) -> u8 {
self.person.age()
}
}
fn main() {
let p = Person::new(42);
let c = Child::new(7, true);
println!("I am {}", p.age());
println!("My child is {}", c.age());
}
Вы можете просто вставить одну структуру в другую. Макет памяти хорош и компактен, но вы должны вручную делегировать все методы от Person
до Child
или одолжить &Person
.
-
Черты характера
trait SayHi {
fn say_hi(&self);
}
struct Person {
age: u8,
}
struct Child {
age: u8,
has_toy: bool,
}
impl SayHi for Person {
fn say_hi(&self) {
println!("Greetings. I am {}", self.age)
}
}
impl SayHi for Child {
fn say_hi(&self) {
if self.has_toy {
println!("I'm only {}, but I have a toy!", self.age)
} else {
println!("I'm only {}, and I don't even have a toy!", self.age)
}
}
}
fn greet<T>(thing: T)
where T: SayHi
{
thing.say_hi()
}
fn main() {
let p = Person { age: 42 };
let c = Child { age: 7, has_toy: true };
greet(p);
greet(c);
}
Вы можете объединить эти две концепции, конечно.
Как DK. упоминает, вы можете выбрать Deref
или DerefMut
. Однако я не согласен с тем, что эти черты должны использоваться таким образом. Мой аргумент сродни аргументу о том, что использование классического объектно-ориентированного наследования просто для повторного использования кода - это неправильная вещь. "Использовать композицию над наследованием" = > "одобрить композицию над Deref
". Тем не менее, я действительно надеюсь на языковую функцию, которая позволяет лаконичную делегацию, уменьшая раздражение композиции.
Ответ 2
Rust не имеет наследования структуры любого типа. Если вы хотите, чтобы StructB
содержал те же поля, что и StructA
, вам нужно использовать композицию.
struct StructB {
a: StructA,
// other fields...
}
Кроме того, чтобы уточнить, черты могут определять только методы и связанные с ними типы; они не могут определять поля.
Если вы хотите использовать StructB
как StructA
, вы можете получить часть этого пути, выполнив Deref
и DerefMut
, что позволит компилятору непрямо указывать указатели на StructB
на указатели на StructA
s:
struct StructA;
impl StructA {
fn name(&self) -> &'static str {
"Anna"
}
}
struct StructB {
a: StructA,
// other fields...
}
impl std::ops::Deref for StructB {
type Target = StructA;
fn deref(&self) -> &Self::Target {
&self.a
}
}
fn main() {
let b = StructB { a: StructA };
println!("{}", b.name());
}
Ответ 3
Реализация Deref
для умных указателей, которые мы можем получить доступ к данным позади, вроде как наследовать.
struct Person {
gender: &'static str,
age: &'static i32
}
struct Child(Person);
impl std::ops::Deref for Child {
type Target = Person;
fn deref(&self) -> &Self -> &Self::Target {
&self.0
}
}
fn main() {
let child = Child(Person{
gender: 'male',
age: 6_i32
});
println!("your child is {}, {} years old.", child.gender, child.age);
}