How do I create a heterogeneous collection of objects? - rust

I want to use trait objects in a Vec. In C++ I could make a base class Thing from which is derived Monster1 and Monster2. I could then create a std::vector<Thing*>. Thing objects must store some data e.g. x : int, y : int, but derived classes need to add more data.
Currently I have something like
struct Level {
// some stuff here
pub things: Vec<Box<ThingTrait + 'static>>,
}
struct ThingRecord {
x: i32,
y: i32,
}
struct Monster1 {
thing_record: ThingRecord,
num_arrows: i32,
}
struct Monster2 {
thing_record: ThingRecord,
num_fireballs: i32,
}
I define a ThingTrait with methods for get_thing_record(), attack(), make_noise() etc. and implement them for Monster1 and Monster2.

Trait objects
The most extensible way to implement a heterogeneous collection (in this case a vector) of objects is exactly what you have:
Vec<Box<dyn ThingTrait + 'static>>
Although there are times where you might want a lifetime that's not 'static, so you'd need something like:
Vec<Box<dyn ThingTrait + 'a>>
You could also have a collection of references to traits, instead of boxed traits:
Vec<&dyn ThingTrait>
An example:
trait ThingTrait {
fn attack(&self);
}
impl ThingTrait for Monster1 {
fn attack(&self) {
println!("monster 1 attacks")
}
}
impl ThingTrait for Monster2 {
fn attack(&self) {
println!("monster 2 attacks")
}
}
fn main() {
let m1 = Monster1 {
thing_record: ThingRecord { x: 42, y: 32 },
num_arrows: 2,
};
let m2 = Monster2 {
thing_record: ThingRecord { x: 42, y: 32 },
num_fireballs: 65,
};
let things: Vec<Box<dyn ThingTrait>> = vec![Box::new(m1), Box::new(m2)];
}
Box<dyn SomeTrait>, Rc<dyn SomeTrait>, &dyn SomeTrait, etc. are all trait objects. These allow implementation of the trait on an infinite number of types, but the tradeoff is that it requires some amount of indirection and dynamic dispatch.
See also:
What makes something a "trait object"?
What does "dyn" mean in a type?
Enums
As mentioned in the comments, if you have a fixed number of known alternatives, a less open-ended solution is to use an enum. This doesn't require that the values be Boxed, but it will still have a small amount of dynamic dispatch to decide which concrete enum variant is present at runtime:
enum Monster {
One(Monster1),
Two(Monster2),
}
impl Monster {
fn attack(&self) {
match *self {
Monster::One(_) => println!("monster 1 attacks"),
Monster::Two(_) => println!("monster 2 attacks"),
}
}
}
fn main() {
let m1 = Monster1 {
thing_record: ThingRecord { x: 42, y: 32 },
num_arrows: 2,
};
let m2 = Monster2 {
thing_record: ThingRecord { x: 42, y: 32 },
num_fireballs: 65,
};
let things = vec![Monster::One(m1), Monster::Two(m2)];
}
See also:
Why does an enum require extra memory size?

Related

Late type in Rust

I'm working with two crates: A and B. I control both. I'd like to create a struct in A that has a field whose type is known only to B (i.e., A is independent of B, but B is dependent on A).
crate_a:
#[derive(Clone)]
pub struct Thing {
pub foo: i32,
pub bar: *const i32,
}
impl Thing {
fn new(x: i32) -> Self {
Thing { foo: x, bar: &0 }
}
}
crate_b:
struct Value {};
fn func1() {
let mut x = A::Thing::new(1);
let y = Value {};
x.bar = &y as *const Value as *const i32;
...
}
fn func2() {
...
let y = unsafe { &*(x.bar as *const Value) };
...
}
This works, but it doesn't feel very "rusty". Is there a cleaner way to do this? I thought about using a trait object, but ran into issues with Clone.
Note: My reason for splitting these out is that the dependencies in B make compilation very slow. Value above is actually from llvm_sys. I'd rather not leak that into A, which has no other dependency on llvm.
The standard way to implement something like this is with generics, which are kind of like type variables: they can be "assigned" a particular type, possibly within some constraints. This is how the standard library can provide types like Vec that work with types that you declare in your crate.
Basically, generics allow Thing to be defined in terms of "some unknown type that will become known later when this type is actually used."
Given the example in your code, it looks like Thing's bar field may or may not be set, which suggests that the built-in Option enum should be used. All you have to do is put a type parameter on Thing and pass that through to Option, like so:
pub mod A {
#[derive(Clone)]
pub struct Thing<T> {
pub foo: i32,
pub bar: Option<T>,
}
impl<T> Thing<T> {
pub fn new(x: i32) -> Self {
Thing { foo: x, bar: None }
}
}
}
pub mod B {
use crate::A;
struct Value;
fn func1() {
let mut x = A::Thing::new(1);
let y = Value;
x.bar = Some(y);
// ...
}
fn func2(x: &A::Thing<Value>) {
// ...
let y: &Value = x.bar.as_ref().unwrap();
// ...
}
}
(Playground)
Here, the x in B::func1() has the type Thing<Value>. You can see with this syntax how Value is substituted for T, which makes the bar field Option<Value>.
If Thing's bar isn't actually supposed to be optional, just write pub bar: T instead, and accept a T in Thing::new() to initialize it:
pub mod A {
#[derive(Clone)]
pub struct Thing<T> {
pub foo: i32,
pub bar: T,
}
impl<T> Thing<T> {
pub fn new(x: i32, y: T) -> Self {
Thing { foo: x, bar: y }
}
}
}
pub mod B {
use crate::A;
struct Value;
fn func1() {
let mut x = A::Thing::new(1, Value);
// ...
}
fn func2(x: &A::Thing<Value>) {
// ...
let y: &Value = &x.bar;
// ...
}
}
(Playground)
Note that the definition of Thing in both of these cases doesn't actually require that T implement Clone; however, Thing<T> will only implement Clone if T also does. #[derive(Clone)] will generate an implementation like:
impl<T> Clone for Thing<T> where T: Clone { /* ... */ }
This can allow your type to be more flexible -- it can now be used in contexts that don't require T to implement Clone, while also being cloneable when T does implement Clone. You get the best of both worlds this way.

How to define a trait that returns a struct that references internal fields

I am trying to define a trait for algorithm that returns a struct that reference multiple internal fields after each step in the algorithm. Then the calling code can optionally choose to serialize the state information or maybe display it. I want to return this as references so that if the calling code doesn't use it, there is not hit to performance (the internal state could be very large).
Below is what I think I want, but instead I want it to be generic of an Algorithm trait and then implementing the Algorithm trait for FooAlgorithm, or others.
use serde::{Deserialize, Serialize}; // 1.0.125
struct FooStateRef<'a> {
field_0: &'a usize,
field_1: &'a usize,
}
impl<'a> Clone for FooStateRef<'a> {
fn clone(&self) -> Self {
FooStateRef {
field_0: self.field_0,
field_1: self.field_1,
}
}
}
impl<'a> Copy for FooStateRef<'a> {}
#[derive(Debug, Serialize, Deserialize)]
struct FooState {
field_0: usize,
field_1: usize,
}
struct FooAlgorithm {
field_0: usize, // Don't want to clone unless the calling code wants to serialize
field_1: usize, // Don't want to clone unless the calling code wants to serialize
field_2: usize, // No need to serialize
}
impl<'a> From<FooStateRef<'a>> for FooState {
fn from(state: FooStateRef) -> FooState {
FooState {
field_0: state.field_0.clone(),
field_1: state.field_1.clone(),
}
}
}
impl FooAlgorithm {
fn step(&mut self) -> Option<FooStateRef> {
self.field_1 += self.field_0;
self.field_2 += self.field_1;
self.field_0 += self.field_2;
if self.field_2 > 100 {
Some(FooStateRef {
field_0: &self.field_0,
field_1: &self.field_1,
})
} else {
None
}
}
}
fn main() {
let mut algo = FooAlgorithm {
field_0: 1,
field_1: 1,
field_2: 1,
};
let mut count = 0;
loop {
match algo.step() {
Some(state_ref) => {
if count % 10 == 0 {
// Save state to file
}
}
None => break,
}
count += 1;
}
}
My attempt at making the Algorithm trait is here: https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=5a36c6eb121f0724284bf7d5ec5a8cad
The issue I'm running into is with lifetimes. How do I write the function signature in the algorithm trait definition so that I can return a FooStateRef<'a> when I implement Algorithm for FooAlgorithm, but also let me return BarStateRef<'a> when I implement Algorithm for BarAlgorithm?
Another thing I have looked into is associated types, but from what I can tell, I would need Generic Associated Traits to add something like type AlgoStateRef<'a>: StateRef<'a>; to the Algorithm trait definition.

Idiomatic way to Find Struct in Struct Vec, then Perform Trait Function on that Struct in Rust

In my project I'm frequently iterating through a vector of structs to find an object by some field value, then use some trait function on that object:
pub struct Person{
name: String,
age: u32,
id: u32,
}
impl Person{
pub fn new(name: String, id_num: u32, age: u32)->Self{
let p = Person{
name: name,
id: id_num,
age: age,
};
p
}
}
trait PersonTrait{
fn printname();
fn get_name()->String;
fn get_age()->u32;
fn set_age(age: u32);
}
impl PersonTrait for Person{
fn printname(){
dbg!(self.name)
}
fn get_name()->String{
self.name
}
fn get_id()->u32{
self.id;
}
fn set_age(age: u32){
self.age = age;
}
}
fn main(){
let my_people = vec![Person::new("Rakim".to_string(), 1, 56), Person::new("Ghostface".to_string(), 2, 56), Person::new("RZA".to_string(), 3, 56)];
//frequently repeating this pattern of finding struct in array of structs, then doing something to that found struct
for person in my_people.clone(){
if person.get_id() == 1 {
person.set_age(100);
}
}
for person in my_people.clone(){
if person.get_id() == "Rakim".to_string(){
person.printname();
}
}
}
So the general pattern im using here is:
for x in my_objects{
if x.id() == some_id{
x.do_some_trait_function()
}
}
I'd like to create a more general function to make this syntax simpler, something like:
//not sure what the correct syntax would be here, or how you might pass a trait function as an argument
fn find_then_do_trait_function(obj_list: Vec<Person>, id: u32, trait_function: my_trait_function()){
for x in obj_list(){
if x.get_id() == id {
//use my trait function on x
}
}
}
How might I do this? I know I could create an enum for every trait function, then match that enum, but that also seems pretty verbose.
There's nothing unique about trait functions. You've identified a very common pattern which can be split into two pieces: we want to filter a vector and then perform some operation on each matching element. We can define a function that takes two closure arguments to do this for us.
fn search_and_call<T>(obj_list: &mut Vec<T>,
mut condition: impl FnMut(&mut T) -> bool,
mut func: impl FnMut(&mut T) -> ()) {
for x in obj_list {
if condition(x) {
func(x);
}
}
}
func can be any closure. That closure might call a trait function, or it might print to the screen, or do any number of things. The writer of the above function needn't care; it's all the same as far as we're concerned. Sample usage:
let mut v = vec!(1, 2, 3, 4);
search_and_call(&mut v, |x| *x % 2 == 0, |x| println!("{}", *x));
It's worth noting, however, that Rust's excellent Iterator trait defines a ton of useful functions, and we can get this behavior for free, without even touching a for loop.
let mut v = vec!(1, 2, 3, 4);
v.iter().filter(|x| *x % 2 == 0).for_each(|x| println!("{}", *x));
.iter() gets an iterator over our vector, .filter(...) produces a new iterator that selects certain elements based on our condition, and .for_each(...) calls a function on all elements which remain after the filter.

How to instantiate the same struct with different implementations of a trait?

I have a trait that is implemented by the same struct in different ways. In order to accomplish this I have different structs that have different implementations. For abstraction I'm going to call these structs A-Z:
trait Trait {
fn bar<T: Trait> (&self, z: &Struct3<T>) -> Struct3<T>;
}
struct StructA {
name: String,
color: String,
}
impl Trait for StructA {
fn bar<T: Trait> (&self, z: &Struct3<T>) -> Struct3<T>{
let mut list = Vec::new();
list.push(self.clone());
let y = Struct2::<T> {
name: z.y.name,
list: list,
};
Struct3::<T> {
point: 1,
y: y,
}
}
}
struct StructZ {
name: String,
color: String,
}
impl Trait for StructZ {
fn bar<T: Trait> (&self, z: &Struct3<T>) -> Struct3<T>{
let mut list = Vec::new();
list.push(self.clone());
let y = Struct2::<T> {
name: z.y.name,
list: list,
};
Struct3::<T> {
point: 26,
y: y,
}
}
}
Is there another way to approach this so that each instance of the struct has a different implementation of the trait, or is making a new struct the best way to go?
I am new to compiled languages. Most of the work I have done has been using Python and TypeScript.
One way to do that is to make your trait generic over some type (even if this type is actually not used inside of the trait).
trait Trait<T> { /* ... */ }
That enables you to implement it multiple times for the same struct.
impl Trait<bool> for Struct { /* ... */ }
impl Trait<u32> for Struct { /* ... */ }
Here, bool and u32 just become compile time flags to chose the implementation of Trait that you want to use.
let struct_3a = Trait::<bool>::bar(&my_object, /* ... */);
let struct_3b = Trait::<u32>::bar(&my_object, /* ... */);
I used bool and u32 here, but you can use your own types here to make everything clearer.

Is there an idiomatic way to implement the component pattern?

Basically a object (struct) is constructed by composing different components. Each concrete component being easily swapped by another component matching the interface (I guess trait).
I'm currently trying to implement with traits which got me into some errors and made me start thinking if this is a common thing in Rust.
// usage example
fn main() {
let obj = MainObject::new(Component1::new(), Component2::new(), Component3());
// Where each component is a type(struct) with some well predefined methods.
}
The main idea behind this is to implement the Component pattern commonly used in games. Basically the game would contain a lot of different objects, with slight variations in behavior and contained data. Instead of having a big class hierarchy, the objects are composed of standard components, more complete example would be.
pub struct Container
{
input: InputHandlerComponent, // Probably a trait
physics: PhysicsComponent, // Probably a trait
renderer: RendererCompoent // Probably a trait
}
impl Container {
fn new(p: PhysicsComponent, i: InputComponent, r: RenderComponent) -> Container {
Container {input: i, physics: p, renderer: r}
}
}
struct ConcretePhysicsComponent;
impl PhysicsComponent for ConcretePhysicsComponent
{
// ...
}
struct ConcreteInputComponent;
impl InputComponent for ConcreteInputComponent
{
// ...
}
struct ConcreteRendererComponent;
impl RendererComponent for ConcreteRendererComponent
{
// ...
}
struct AnotherConcreteRendererComponent;
impl RendererComponent for AnotherConcreteRendererComponent
{
// ...
}
// usage example
fn main() {
let obj = Container::new(ConcreteInputComponent::new(), ConcretePhysicsComponent::new(), ConcreteRendererComponent::new());
// Where each component is a type(struct) with some well predefined methods.
// This is a slightly modified version of this object, with changed rendering behaviour
let obj2 = Container::new(ConcreteInputComponent::new(), ConcretePhysicsComponent::new(), AnotherConcreteRendererComponent::new()); }
It sounds like you are just asking about traits, multiple concrete implementations of that trait, and a wrapper object that restricts itself to types that implement that trait. Optionally, the container can implement the trait by delegating it to the inner object.
trait Health {
fn life(&self) -> u8;
fn hit_for(&mut self, lost_life: u8);
}
#[derive(Debug, Copy, Clone)]
struct WimpyHealth(u8);
impl Health for WimpyHealth {
fn life(&self) -> u8 { self.0 }
fn hit_for(&mut self, lost_life: u8) { self.0 -= lost_life * 2; }
}
#[derive(Debug, Copy, Clone)]
struct BuffHealth(u8);
impl Health for BuffHealth {
fn life(&self) -> u8 { self.0 }
fn hit_for(&mut self, lost_life: u8) { self.0 -= lost_life / 2; }
}
#[derive(Debug, Copy, Clone)]
struct Player<H> {
health: H,
}
impl<H> Health for Player<H>
where H: Health
{
fn life(&self) -> u8 { self.health.life() }
fn hit_for(&mut self, lost_life: u8) { self.health.hit_for(lost_life) }
}
fn main() {
let mut player_one = Player { health: WimpyHealth(128) };
let mut player_two = Player { health: BuffHealth(128) };
player_one.hit_for(12);
player_two.hit_for(12);
println!("{:?}", player_one);
println!("{:?}", player_two);
}
it is not possible to have an array of such Players without using Boxed values
That's correct. An array or vector (or any generic type, really) needs to all be of the same type. This is especially important for arrays/vectors because their memory layout is contiguous and each item needs to be at a fixed interval.
If you were allowed to have different types, then you could have one player that had a health that took 1 byte and another player with health that took 2 bytes. Then all the offsets would be incorrect.
You can implement the Health trait for a Box<Health>, and then the Player objects can be stored sequentially, but they would each have a pointer to the appropriate concrete implementation of Health via the box.
impl<H: ?Sized> Health for Box<H>
where H: Health
{
fn life(&self) -> u8 { (**self).life() }
fn hit_for(&mut self, lost_life: u8) { (**self).hit_for(lost_life) }
}
fn main() {
let mut players = vec![
Player { health: Box::new(WimpyHealth(128)) as Box<Health> },
Player { health: Box::new(BuffHealth(128)) as Box<Health> }
];
for player in players.iter_mut() {
player.hit_for(42);
}
println!("{:?}", players[0].life());
println!("{:?}", players[1].life());
}

Resources