Как получить доступ к полям структуры из реализованной функции типажа в Rust?

Итак, следуя онлайн-учебнику, я хотел расширить функциональность черты Canine, используя поле name из структуры, в которой реализована черта:

struct Animal {
    name: String,
}

impl Animal {
    fn eat(&self) {
        println!("{} is eating", &self.name)
    }

    fn breathe(&self) {
        println!("{} is breathing", &self.name)
    }
}

trait Canine {

    fn bark(&self) {
        println!("Woof woof!");
    }
    fn run(&self) {
        println!("{} is running.", &self.name);
    }

}

impl Canine for Animal {}

fn main() {
    let rover: Animal = Animal {
        name: String::from("Rover")
    };

    // rover can do these as an animal
    rover.eat();
    rover.breathe();

    // rover can additionally do these as a canine
    rover.bark();
    rover.run();

}

Этот код работает, за исключением одного компонента: метода run() в трейте Canine. Ошибка компилятора:

error[E0609]: no field `name` on type `&Self`
  --> src/main.rs:21:42
   |
15 | trait Canine {
   | ------------ type parameter 'Self' declared here
...
21 |         println!("{} is running.", &self.name);
   |                                          ^^^^

For more information about this error, try `rustc --explain E0609`.

Почему я могу использовать &self.name в определениях методов базовой реализации для структуры Animal, но не в трейте Canine? Каким будет правильный способ сделать это?

Заранее спасибо!

Вы не можете. Вы должны определить метод получения.

PitaJ 02.05.2023 18:34
Почему Python в конце концов умрет
Почему Python в конце концов умрет
Последние 20 лет были действительно хорошими для Python. Он прошел путь от "просто языка сценариев" до основного языка, используемого для написания...
0
1
57
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Черта Canine должна определять функции, но не реализации, а затем вы помещаете эти реализации в блок impl Canine for Animal.

trait Canine {
    fn bark(&self);
    fn run(&self);
}

impl Canine for Animal {
    fn bark(&self) {
        println!("Woof woof!");
    }
    fn run(&self) {
        println!("{} is running.", &self.name);
    }
}

Теперь компилятор знает, что такое self.name, так как это явно Animal. В исходном коде неизвестно, что это такое, так как это просто трейт без контекста.

Альтернативой является определение какого-либо другого признака, такого как Named, который можно использовать в качестве зависимости:

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

trait Canine : Named {
    fn run(&self) {
        println!("{} is running.", self.name());
    }
}

Спасибо! Это очень полезно. Мне особенно нравится альтернатива, допускающая реализацию по умолчанию. т. е. нет собачьих растений, есть только животные, поэтому я хочу, чтобы было ясно, что Canine когда-либо будет реализован только для структур Animal.

jkix 02.05.2023 18:44

Rust сильно зависит от композиции, поэтому вам может понадобиться основа Creature, для которой вы затем impl Animal и т. д.

tadman 02.05.2023 18:51

Имеет смысл. Также похоже, что есть как когерентность, так и запечатанные черты, которые являются другими подходами к этому шаблону: api-рекомендации/…

jkix 02.05.2023 18:53

Другие вопросы по теме