Ákos
Ákos

Reputation: 69

How to create a static array of objects that implement a common trait?

I am trying to create a static array of objects that implement a common trait. All these structs and their sizes are known at compile time. But when accessing a field defined on the struct the compiler tells me that the field is not on the type.

fn main() {
    for &thing in ALL_THINGS {
        println!("{}", thing.name)
    }
}
trait Thing: Sync { }

struct SpecificThing { 
    name: &'static str
}
impl Thing for SpecificThing { }

static ALL_THINGS: &'static [&dyn Thing] = &[&SpecificThing {name: "test"}];

error[E0609]: no field `name` on type `&dyn Thing`
 --> src/main.rs:3:30
  |
3 |         println!("{}", thing.name)
  |                              ^^^^

Example: https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=28a29e98cadf97edb8d4ec61703e8959

The questions static array of trait objects, Create vector of objects implementing a trait in Rust, Can I have a static borrowed reference to a trait object? or Vector of objects belonging to a trait doesn't help with explaining why this happens or how to resolve it.

Please what am I doing wrong here? Is there a better method to solve this task that I have not found yet?

Upvotes: 3

Views: 1789

Answers (2)

Dmitry
Dmitry

Reputation: 1637

When you define &dyn Thing, you erase all the information about specific data type. That means you can't access fields of dinamically dispatched objects.

Just imagine that you have two different structs in ALL_THINGS:

struct SpecificThing { 
    name: &'static str
}
struct SpecificAnotherThing { 
    no_name: &'static str
}

static ALL_THINGS: &'static [&dyn Thing] = &[&SpecificThing {name: "test"}, &SpecificAnotherThing { no_name: "" }];

You can't access name field because trait Thing know nothing about concrete types it implemented for. Therefore you can't access it's fields directly.

If you really need it, you should define a method in Thing trait which will return value you need:

trait Thing: Sync {
   fn name(&self) -> &str;
}
// ...
// ...
impl Thing for SpecificThing {
   fn name(&self) -> &str {
       self.name
   }
}

Or you can use static dispatching and algebraic data types (enum).

Upvotes: 3

Emoun
Emoun

Reputation: 2507

You can't access SpecificThing.name from a &dyn Thing since not all Things have a name field (ignoring the fact that traits don't have fields).

Your use of dyn Thing suggests you have a set of objects (structs/enums) that have some things in common. All these commonalities must be present in Thing for you to access them. For example, if a name is a common thing, you could add a function that gets the name:

fn main() {
    for &thing in ALL_THINGS {
        println!("{}", thing.get_name())
    }
}
trait Thing: Sync { 
    fn get_name(&self) -> &'static str;
}

struct SpecificThing { 
    name: &'static str
}
impl Thing for SpecificThing {
    fn get_name(&self) -> &'static str {
        self.name
    }
    
}

static ALL_THINGS: &'static [&dyn Thing] = &[&SpecificThing {name: "test"}];

Upvotes: 1

Related Questions