I'm doing some statistical things using the statrs crate. It implements a number of distribution functions and my input can be one of them, and not always the same.
I would like to have something that in pseudo-code looks like this:
let curve = create_curve (from parameters);
curve.sample(some point);
curve.pdf(some point);
Unfortunately, the two functions I need, sample() and pdf() are implemented in two different traits (Distribution and Continuous) and rust can't return multiple traits. So I've been wrecking my brain trying to figure out a way to do this kind of abstract handling. It gets a bit more tricky as both the sample() and the pdf() are inside their own seperate functions because I do additional processing, so I also need to pass the curve into them.
The basic idea is that I want to have a code that goes "ok, here is a curve, it has these traits and I'll use them without worrying which type of curve it is". The exact thing that traits should do, but the multiple traits thing is making it impossible.
You can make a blanket impl for all types that implement both super traits. Now implementors guarantee to support both behaviors. I don't know if your types do however.
Otherwise you can create a struct/enum Curve which will abstract out the differences, like returning an Option for methods that might not be available etc.
I found that somewhere and tried it as well, could not get it to work.
Is there a working example of something like this? All I find on the Internet is basically "do this", but never the whole thing with parameter passing and all.
Note that the method returns a Box as impl Trait in return type always has to return the exact same type, and I suppose that if you had only one type, you wouldn't be asking this. If you don't want to pay for dynamic dispatch, I suppose you'd be limited to something like enum_dispatch.
maybe I adapted it badly. I'm still largely ignorant in rust:
trait Both: Distribution<f64> + Continuous<f64, f64> {}
impl<T> Both for T where T: Distribution<f64> + Continuous<f64, f64> {}
fn curve( which: &str ) -> Box< dyn Both >
{
match which
{
"A" => Box::new( Beta::new(1.5, 0.5) ),
"B" => Box::new( LogNormal::new(1.5, 0.5) ),
_ => panic!( "Unknown type provided" ),
}
}
fn main()
{
let curve = curve( "A" );
// you can call methods from both Sample and Pdf on curve here.
}
I may have to be more specific:
use statrs::distribution::{Beta, LogNormal, Continuous};
use rand::distributions::Distribution;
The function sample() is defined on the Distribution trait, while the function pdf() is defined on the Continuous trait. That's basically the source of all my troubles.
I also now see that these traits come from completely different crates. I would personally make a facade to this, like a struct/trait which has all the methods you want to use, and which hides the specifics of the libraries you use. It seems hard to know for sure that a type implements unrelated traits from two different crates.
Make your own type and a potentially a trait that has all the methods you want to use. Then impl that trait for your type. This way you completely control the situation and your trait will be object safe if you want/need it to be. Also, the compiler will be able to guide you better.