I have a need for a common interface to some OS-specific code. These implementations are rather large, with not much common between them, so my initial though was to create separate structs for each implementation, both of which implement a common trait to make it agnostic to the implementation.
First off, is this the right way to go? Is there some other means that might make this easier in Rust?
Next, I want to iterate on this, which means storing a reference to a trait, which I haven't been able to get right. If part one of this question is correct, should I be attempting to create an iterator against the common trait, or should I be making OS-specific versions of the iterator as well?
Here's a simple of example of what I'm trying to do:
trait Thing {
#[cfg(any(target_os = "linux"))]
fn new() -> Thing {
LinuxThing {
value: 1
}
}
#[cfg(any(target_os = "macos"))]
fn new() -> Thing {
MacThing {
value: 2
}
}
fn iter(&self) -> ThingIter {
ThingIter {
thing: self
}
}
fn stuff(&self) -> u8;
}
#[cfg(any(target_os = "linux"))]
struct LinuxThing {
value: u8,
}
#[cfg(any(target_os = "macos"))]
struct MacThing {
value: u8,
}
#[cfg(any(target_os = "linux"))]
impl Thing for LinuxThing {
fn stuff(&self) -> u8 {
self.value
}
}
#[cfg(any(target_os = "macos"))]
impl Thing for MacThing {
fn stuff(&self) -> u8 {
self.value * 2
}
}
struct ThingIter<'a> {
thing: &'a Thing,
}
impl<'a> Iterator for ThingIter<'a> {
type Item = u8;
fn next(&mut self) -> Option<Self::Item> {
Some(self.stuff())
}
}
fn main() {
let thing = Thing::new();
println!("Thing: {}", thing.stuff());
let iter = ThingIter { thing: &thing };
println!("Next: {}", iter.next());
println!("Next: {}", iter.next());
println!("Next: {}", iter.next());
}
This is currently failing with:
error[E0038]: the trait `Thing` cannot be made into an object
--> test.rs:50:5
|
50 | thing: &'a Thing,
| ^^^^^^^^^^^^^^^^ the trait `Thing` cannot be made into an object
|
= note: method `new` has no receiver
error[E0038]: the trait `Thing` cannot be made into an object
--> test.rs:10:5
|
10 | fn new() -> Thing {
| ^^^^^^^^^^^^^^^^^ the trait `Thing` cannot be made into an object
|
= note: method `new` has no receiver
error: aborting due to 2 previous errors
I'm a C developer by trade, which means I'm used to just doing whatever I want. I love the concept of Rust, but C is a hard habit to break.