In Hoc Signo
In Hoc Signo

Reputation: 495

How to access fields of dyn structs?

Recently I've been working on learning advanced Rust. As part of that, I'm learning to use dynamic dispatch.

In my tinkering I've run across a bit of an issue. For some reason, I can't seem to access fields of structs which have been assigned to variables using Boxes and dynamic dispatch. For example,

fn main() {
    let z: Box<dyn S>;
    z = Box::new(A::new());

    println!("{}", z.val);
}

trait S {
    fn new () -> Self where Self: Sized;
}


struct A {
    val: i32,
}

impl S for A {
    fn new () -> A {
        A {val: 1}
    }
}

struct B {
    val: i32
}

impl S for B {
    fn new() -> B {
        B {val:2}
    }
}

yields the error message "error[E0609]: no field val on type Box<dyn S>"

Is there any way to access such fields, or do I need to kluge together a workaround?

Upvotes: 4

Views: 1848

Answers (1)

Maxim Gritsenko
Maxim Gritsenko

Reputation: 2592

It is easy to understand why this does not work if you understand what a trait object is. When a method returns a dyn Trait it does not return an instance of any struct. Instead it returns a lookup table, which tells the caller where to find its methods. So the caller can access methods without knowing anything about underlying struct itself.

So if the caller does not have access to the struct itself it's clear it cannot access its fields.

There are two ways to implement what you are trying to do:

  1. Use methods of the trait to access required fields. This is a good option if the list of possible implementors of the trait is not predetermined, and if the trait itself is not too complex (as traits that can be represented as trait objects have some limitations). Not though that trait objects come with some runtime overhead (using lookup tables is slower then direct method access).
  2. Use enum. If you know complete list of options that the method can return, it's the best and the most Rust'y solution.
enum S {
  A { val: i32, anotherAval: u32 },
  B { val: i32, anotherBval: f32 },
}

impl S {
  fn val(&self) -> i32 {
    match self {
      S::A => A.val,
      S::B => B.val,
    }
  }
}

Upvotes: 5

Related Questions