0

I see some related questions (like this and this) but I'm hoping that my use case for default methods is unique enough to ask a slightly different question. The following minimal example works and outputs "Sheriff Ted" shot "Billy the Kid"!:

#[derive(Debug)]
struct Actor {
    name: String,
}

fn main() {
    let cop = Actor {
        name: String::from("Sheriff Ted"),
    };

    let robber = Actor {
        name: String::from("Billy the Kid")
    };

    println!("{:?} shot {:?}!", cop.name, robber.name); // without the trait. with:
    // cop.shoot(&robber);
}

//pub trait Shoot {
//    fn shoot(&self, other: &Actor) {
//        println!("\n{:?} shot {:?}!",
//                 &self.name,
//                 &other.name,
//        )
//    }
//}
//
//impl Shoot for Actor {}

As you can see, I want impart the Shoot implementation and the shoot method it contains on the Actor struct. When I uncomment the Shoot trait, its implementation on Actor, and the call cop.shoot(&robber), I get the error message related questions have gotten, as well: error[E0609]: no field 'name' on type '&Self'.

My first thought was to specify &self: Actor in the default method's signature, but that yields the delimiter error, so isn't syntactically valid.

I think this question is unique because the other questions seem to misunderstand how the generics they specify are shadowing their intended types, and in my case I'm not understanding why I can't access fields within the structs on which I am trying to implement a default method.

This works for cases when only Actors need to shoot, but I am looking for a way to apply this behavior (right now, just printlning) across multiple types.

impl Actor {
    fn shoot(&self, other: &Actor) {
        println!("\n{:?} shot {:?}!",
                 self.name,
                 other.name,
        )
    }
}
Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
d8aninja
  • 3,233
  • 4
  • 36
  • 60
  • I believe your question is answered by the answers of [Is it possible to access struct fields from within a trait?](https://stackoverflow.com/q/28219730/155423). If you disagree, please **[edit]** your question to explain the differences. Otherwise, we can mark this question as already answered. – Shepmaster Apr 14 '19 at 18:52
  • 1
    @Shepmaster none of those answers I reference in the top of my question made it clear (to me, anyway) that struct fields could not be accessed from a default method definition because of the erroneous assumption that the struct would have that field (still sort of probing how to use a bound to ensure the use of a struct type that had said field). trying to get at this distinction in my answer, below. if I'm in error then we can mark as dupe. EDIT: I see it now, `default implementation of a trait (i.e. defining a method body within the trait), then no, you can't access fields.` – d8aninja Apr 14 '19 at 19:32
  • 1
    It's also answered by the excellent response, [here](https://stackoverflow.com/questions/39150216/implementing-a-trait-for-multiple-types-at-once): `A trait cannot expose a field/attribute...(cont)` – d8aninja Apr 14 '19 at 21:39

2 Answers2

2

You are not trying to implement a default method on any structs; you are implementing it for the trait. Therefore you can't access any fields on any structs; you can only access what the trait demands.

A default implementation of a trait methods means that any type that implements the non-defaulted methods of the trait can use the default method, no matter what it looks like otherwise. But you expect that the implementing type has a name field in addition to what the trait demands (it demands nothing, by the way).

This is simply not a valid assumption.

I wonder why you're using a trait here at all. If you are okay with requiring self to be Actor in the shoot method, why is it a method of a trait? Why isn't it an inherent method of the Actor struct without any traits?

Sebastian Redl
  • 69,373
  • 8
  • 123
  • 157
  • I pared down my example to make it minimal, but many other things shoot as well. Gun, cannon, or ship structs (all of which would have a name field) could have this trait implemented on them...does that make sense? If not, are you saying I would constrain the trait's implementation only to things that have this name field? (Added a small edit in reference to your suggestion...) – d8aninja Apr 14 '19 at 17:01
0

After having read Sebastian's response, I think the "answer" is: you can't name struct fields in traits' default methods because you don't know what fields a struct may have until the implementation of the trait. So you'd define an (abstract?) method signature, and then make it concrete when it's implemented. In my case, this works:

trait Shoot {
    fn shoot(&self, other: &Actor);
}

impl Shoot for Actor {
    fn shoot(&self, other: &Actor) {
        println!("\n{:?} shot {:?}!",
            self.name,
            other.name,
        );
    }
}

Still interested to know if I can constrain a trait to be applied only to structs with certain fields and if this is different than "trait bounds". (It is, I think...)

d8aninja
  • 3,233
  • 4
  • 36
  • 60
  • You could add a trait `NamedShooter` which defines a getter method `fn name(&self) -> &str`. Then you can add a blanket implementation of `Shoot` for `NamedShooter` implementations. – Sebastian Redl Apr 14 '19 at 18:22