Using Struct for strategy reusability in iproduct iterator

Hi! I'm trying to use a struct that implements a trait inside a function, the problem is that a move ocurrs and I can reuse that struct. I've made a playground to show the error.
Now, I don't understand why is this happening, as I could easily do the slow equivalent:

let _ = iproduct!(vec_1, vec_2).map(|(num_1, num_2)| {
	if is_all_vs_all || (!is_all_vs_all && num_1 == num_2) {
		correlation_method_struct.compute(num_1, num_2)
	} else {
		0
	}
});

There's no problem with correlation_method_struct being borrowed, even when it's in a cartesian closure. But I don't want to check on every iteration, I just want to use the corresponding function, so the above solution is not the best one. What I'm missing? Any kind of help would be really appreciated

They are not "equivalent" though, at least not with respect to ownership. When you inline your condition, correlation_method_struct is only being borrowed, not moved (since compute takes &self).

Thus, you should change your signatures so that they don't consume unnecessarily. As a rule of thumb, you should almost never take Box<dyn Trait> by value, anyway. There's not much you can do with a boxed trait object that you own, because it's unsized, so any nontrivial methods on it will probably take &self or &mut self. Consequently, unless you want to move it or store it somewhere else, you should just accept a &dyn Trait or &mut dyn Trait instead. (Playground.)


By the way, I recommend you to read up on Rust naming conventions. correlation_method_struct is not a great variable name, because everything may have methods and not every type that impls a trait is a struct. get_…() names are also rarely used, as idioms don't usually rely on "getters" and "setters" as much as they do in some other languages.

1 Like

Thank you so much! It solved the problem. I've tried with & of Box but It didn't work, the trick was on referencing the Struct directly.
With respect to naming convention is wrong even when I know that the variable is a struct?

It works just fine with &Box, too. (However, it's pretty non-idiomatic.)

Yes, it's still non-idiomatic. Rust code generally doesn't care whether a certain variable is a struct or something else. What matters is the capabilities of the type. Hungarian notation is not a Rust thing (and its value is dubious in pretty much all modern, typed programming languages as well).

1 Like

Cool! Thank you again!

There's one other thing you might want to consider. You have an enum with variant names matching existing empty/unit structs. You can put those structs into the enum variants at zero cost:

pub enum CorrelationMethod {
   Method1(Method1),
   Method2(Method2),
   Method3(Method3),
}

It doesn't particularly impact what you're exploring, but if you're looking to export this for use elsewhere or repeat it many times for different calculations it could really simplify your access pathways.

  • Gives you access to the underlying methods from just the single CorrelationMethod
    • Which also allows you to ignore the individual MethodNs you have, and leave them private.
  • This also ensures match arms are in functions implemented on the top-level enum,
    • Rather than in separate/independent functions.
// New methods allow for creation without having Method1/Method2/Method3/etc.
impl CorrelationMethod {
    pub fn new_method1() -> Self { Self::Method1(Method1{}) }
    pub fn new_method2() -> Self { Self::Method2(Method2{}) }
    pub fn new_method3() -> Self { Self::Method3(Method3{}) }
}

impl Correlation for Method {
    fn compute(&self, a: i32, b: i32) -> i32 {
        match &self {
            Self::Method1(method) => method.compute(a, b),
            Self::Method2(method) => method.compute(a, b),
            Self::Method3(method) => method.compute(a, b),
        }
    }
}

Which leads me to a question I've had - is there any way to condense that final match statement knowing that EVERY variant implements the same trait? I presume it's a question of how to tell the compiler that future variants which could break the pattern can't/won't be added.

At the very least, this reduces some repeated bits:

match self {
    Self::Method1(m) | Self::Method2(m) | Self::Method3(m) => m.compute(a, b)
}

And another follow-up question... What forces the use of Box<dyn Trait> vs. just impl Trait as shown here?

Hi! Thank you for your advice!
That Enum was reduced for the example, it's more complex and need diferent struct instantiations which are hidden from the public API. So I defined an Enum to define which strategy needs to be created internally.

I'm using Box<dyn Trait> as possible strategies returned have different types and Rustc is throwing:

if and else/match have incompatible types

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.