Reputation: 5737
In Java-speak, I am trying to create a collection (vector) of objects (strict instances), each one of which implements an interface (trait), so I can then iterate over the collection and call a method on all of them.
I have reduced it down to one sample file below which contains all the parts that I hope will make it easier to get answers.
// main.rs - try and compile using just "rustc main.rs"
use std::io::Result;
/// ////// Part 1
// Types used by implementors of the trait, and in creating a vector of implementors of the trai
pub struct SampleResult {
metric: String,
}
pub trait SampleRunner {
fn run(&self, &'static str) -> Result<SampleResult>;
}
pub struct Sample {
name: &'static str,
runner: &'static SampleRunner,
}
/// /////// Part 2
/// Create one specific static instance of such as Sample
static SAMPLE1: Sample = Sample {
name: "sample",
runner: &Sample1,
};
// need a struct to hold the run method to satisfy the trait???
struct Sample1;
// Implement the trait for this specific struct
impl SampleRunner for Sample1 {
fn run(&self, name: &'static str) -> Result<SampleResult> {
println!("Name: {}", name);
Ok(SampleResult { metric: "OK".to_string() })
}
}
/// /////// Part 3
/// Using the existing static instances of Sample struct, by creating a vector of references for them
/// then iterating over the vector and calling the trait method on each one
fn main() {
let sample_set: Vec<&Sample> = vec![&SAMPLE1];
for sample in sample_set.iter() {
match sample.runner.run(sample.name) {
Ok(result) => println!("Success"),
_ => panic!("failed"),
}
}
}
That particular example fails with the message:
error[E0277]: the trait bound `SampleRunner + 'static: std::marker::Sync` is not satisfied in `Sample`
--> <anon>:21:1
|
21 | static SAMPLE1: Sample = Sample {
| _^ starting here...
22 | | name: "sample",
23 | | runner: &Sample1,
24 | | };
| |__^ ...ending here: within `Sample`, the trait `std::marker::Sync` is not implemented for `SampleRunner + 'static`
|
= note: `SampleRunner + 'static` cannot be shared between threads safely
= note: required because it appears within the type `&'static SampleRunner + 'static`
= note: required because it appears within the type `Sample`
= note: shared static variables must have a type that implements `Sync`
But I have had many different problems depending on the approach I have taken, related to Sync
, Sized
, etc etc.
Upvotes: 1
Views: 4689
Reputation: 29896
There are two little errors in the code. The first is described by the error, it tells us that the static value is not safe as it does not implement the Sync trait. It just tries to prepare for the case when the static value is manipulated from multiple threads. Here, the best solution is simply to mark the value as const
. After that, there is some problem with the lifetime of &SAMPLE1
in main, can be solved by "using the let keyword to increase it's lifetime".
The code after these little modifications compiles, and looks like this:
use std::io::{Result};
pub struct SampleResult {
metric: String
}
pub trait SampleRunner {
fn run(&self, &'static str) -> Result<SampleResult>;
}
pub struct Sample {
name: &'static str,
runner: &'static SampleRunner
}
// Make it const
const SAMPLE1: Sample = Sample { name: "sample", runner: &Sample1 };
struct Sample1;
impl SampleRunner for Sample1 {
fn run(&self, name: &'static str) -> Result<SampleResult> {
println!("Name: {}", name);
Ok(SampleResult {metric: "OK".to_string() })
}
}
fn main() {
// Extend the lifetime of the borrow by assigning it to a scope variable
let borrowed_sample1 : &Sample = &SAMPLE1;
let sample_set: Vec<&Sample> = vec!(borrowed_sample1);
for sample in sample_set.iter() {
match sample.runner.run(sample.name) {
Ok(result) => println!("Success"),
_ => panic!("failed")
}
}
}
However, I see that you are not satisfied with the code as you have to create a new struct for every implementation of SampleRunner
. There is another way, using lambda functions (the Rust docs just refers to them as Closures).
use std::io::{Result};
pub struct SampleResult {
metric: String
}
type SampleRunner = Fn(&'static str) -> Result<SampleResult>;
pub struct Sample {
name: &'static str,
runner: &'static SampleRunner
}
// Still const, use a lambda as runner
const SAMPLE1: Sample = Sample { name: "sample", runner: &|name| {
println!("Name: {}", name);
Ok(SampleResult {metric: "OK".to_string() })
} };
fn main() {
let borrowed_sample1 : &Sample = &SAMPLE1;
let sample_set: Vec<&Sample> = vec!(borrowed_sample1);
for sample in sample_set.iter() {
// Must parenthese sample.runner so rust knows its a field not a method
match (sample.runner)(sample.name) {
Ok(result) => println!("Success"),
_ => panic!("failed")
}
}
}
Upvotes: 2