0

I have a struct:

struct A<T> {
    name: String,
    Value: T,
}

let a = A {
    name: String::from("a"),
    value: 1,
};
let b = A {
    name: String::from("b"),
    value: String::from("b"),
};

let v = Vec::new();

How do I put a and b to v? I want Vec<A> to be a function's return type. I think struct A has wrong statement, but I don't know how to fix it. Use Box?

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
uuz
  • 21
  • 5
  • 2
    `a` and `b` can be treated as diffretent types `A` and `A` for example. They cannot be pushed directly in the vector. You could wrap them in an enum to solve it.... – Netwave May 09 '21 at 16:45
  • Great! Thanks a lot! – uuz May 09 '21 at 16:56

1 Answers1

1

In Rust Generic Types are basically different types. You cannot have a collection of objects which have different generic arguments.

There are two solutions to this problem, depending on your situation.

You know upfront what types are going to be used

If you know what types are going to be used throughout the program, you can create an enum of A<T> with those types:

enum ATypes {
    u32(A<u32>),
    string(A<String>),
}
v.push(ATypes::u32(a));
v.push(ATypes::string(b));

You don't know what types are going to be used

In this case you have to rely on dynamic dispatch. To do this we use Trait objects. First, we have to create a trait for the A's:

trait AnyA {}

Then, we need to implement it for all the A's:

impl<T> AnyA for A<T> {}

Then, Create a Vec of trait objects:

let mut arr: Vec<Box<dyn AnyA>> = Vec::new();

Now, to push something to the Vec do:

arr.push(Box::new(a));
arr.push(Box::new(b)); 

All the methods you want to use on the elements of the Vec have to be implemented on the trait.

Sadly this method is quite limited because of the lack of GATs in Rust

Misty
  • 416
  • 3
  • 8
  • You mention "All the methods you want to use on the elements of the Vec have to be implemented on the trait.", but how to deal with `T`? I can only implement methods, which are defined in `AnyA`-trait (or implement default methods). The generic types bound to methods are not the generic type bound to the `impl AnyA for A`. Maybe I miss something completely, but this is not working: [Playground](https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=986b1a2f1515b5a02e8ea3355b688e07). Rust complains, T is not T. – Josh Sep 01 '22 at 09:29
  • This kind of trait would return a variable which depends on the type that implements it. You can't have a trait who's signature depends on the implementor type in a trait object. You can add as_any to the trait and use its downcast_ref function to get a reference to the concrete type ([playground](https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=1e6548cca00f948dd2fd963844553bbe)) or use the [downcast_rs](https://lib.rs/crates/downcast-rs) crate. You still need some way to identify the type though. – Misty Sep 03 '22 at 10:39