I've been playing with rust and I was doing a very silly program which combined traits, structs, some silly concurrency and generics. Everything was quite understandable for me until I faced some problems when sending a trait between threads.
Firs of all, at one point a realized I needed a vector of Boxed Animals in order to store the different elements that comply with the Animal trait, ok, I get that because the trait is an abstraction of some other particular structs which can vary in "size" etc, so we have to store them in the heap. But then the first weird point for me was that because I had to use a Box to store the traits, then I had to implement my trait for the Boxed trait also (see (*1) on the code comments).
Once I did that, the program was correct for the compiler, but I ran into some problems at runtime that I don't understand. The error that I'm getting is:
thread '<unknown>' has overflowed its stack
fatal runtime error: stack overflow
[1] 4175 abort (core dumped) cargo run
The code is:
use std::thread;
use std::sync::mpsc::{Sender};
use std::sync::mpsc;
use std::time;
trait Animal {
fn poop(&self) -> Poop;
}
#[derive(Debug)]
enum Weight {
VeryLight,
Light,
Medium,
Heavy,
SuperHeavy,
}
#[derive(Debug)]
struct Poop {
shape: String,
weight: Weight,
}
struct Wombat;
impl Animal for Wombat {
fn poop(&self) -> Poop {
Poop {
shape: "cubic".to_string(),
weight: Weight::Light,
}
}
}
struct Cat;
impl Animal for Cat {
fn poop(&self) -> Poop {
Poop {
shape: "cylindrical".to_string(),
weight: Weight::VeryLight,
}
}
}
// (*1) This seemed weird for me and I'm not sure the
// impl es correct
impl Animal for Box<dyn Animal + Send> {
fn poop(&self) -> Poop {
let t: &dyn Animal = self;
// self.poop()
t.poop()
}
}
fn feed_animal<T> (a: T, tx: Sender<String>)
where T: Animal + Send + 'static {
thread::spawn(move || {
thread::sleep(time::Duration::from_secs(2));
tx.send(format!("{:?}", a.poop()))
});
}
fn main() {
let mut animals: Vec<Box<dyn Animal + Send>> = Vec::new();
animals.push(Box::new(Wombat));
animals.push(Box::new(Cat));
let (tx, rx) = mpsc::channel();
for a in animals {
let txi = tx.clone();
feed_animal(a, txi);
}
drop(tx);
for r in rx {
println!("The animal just pooped: {:?}", r);
}
}
I'm a little lost with the error message honestly. Usually when I see this kind of error in some other programing languages is due to an infinite loop that will overflow the stack, but in this case I guess there must be some error in the way I "send" the Boxed trait to the child thread that makes rust handle the child thread stack memory badly at runtime.. I'm not sure.
Any hint to the right direction will be very welcomed. Thank you.