Call a function of a trait that implements a primitive type in tests

I'm having a problem understanding the namespaces in this example:

trait EvaluateExpression {
    fn eval_expr_to_type(val: &String) -> Result<Self, Box<dyn std::error::Error>>
    where
        Self: Sized;
}

impl EvaluateExpression for f64 {
    fn eval_expr_to_type(formula: &String) -> Result<f64, Box<dyn std::error::Error>> {
        Ok(0.0)
    }
}

impl EvaluateExpression for f32 {
    fn eval_expr_to_type(formula: &String) -> Result<f32, Box<dyn std::error::Error>> {
        Ok(0.0)
    }
}

fn do_something()
{
    
}


fn main() {
    do_something();
    let x = f32::eval_expr_to_type(&"".to_string());
}

#[cfg(test)]
mod tests {
    #[test]
    fn basic() {
        crate::do_something();
        let x = crate::f32::eval_expr_to_type(&"".to_string());
    }
}

Here's the code on playground.

This doesn't compile. The problem is the test line with crate::f32. Why does this work when called in main(), but doesn't work when called in tests? And why does it work for the other function but not for this one? Can someone please explain how these scopes work in this case?

f32 should not be prefixed with anything. Just doing f32::eval_expr_to_type should work, even in tests.
Note, you also need to import the relavant traits into the test module, in this case use crate::EvaluateExpression should do the tricks

Please try that out in playground. I provided the code there. That doesn't work.

error[E0599]: no function or associated item named `eval_expr_to_type` found for type `f32` in the current scope
  --> src/lib.rs:35:22
   |
35 |         let x = f32::eval_expr_to_type(&"".to_string());
   |                      ^^^^^^^^^^^^^^^^^ function or associated item not found in `f32`
   |
   = help: items from traits can only be used if the trait is in scope
   = note: the following trait is implemented but not in scope; perhaps add a `use` for it:
           `use crate::EvaluateExpression;`

This error can be fixed by following the instructions, import EvaluateExpression in the test module. Treat inline modules as being declared in a seperate file.

Thanks. Out of curiosity, how can I do it without use? Prefixing with crate::EvaluateExpression doesn't work.

You can use universal function call syntax,

<f32 as crate::EvaluateExpression>::eval_expr_to_type(...)
1 Like

Beautiful! Thanks a lot. This is the missing piece I've been looking for. Now I understand how casting primitive types to traits works. :smile:

Well, this isn't casting anything (even though as is used), it just specifies which trait the function came from. Just in case multiple traits declare the same function.

Still. I just learned this whole thing a couple of hours ago. To me, it's new, because I'm a C++ guy and I would've done all that with a template. The idea that a trait can represent a primitive type is quite new to me :wink:

1 Like

You''ll need to use the "unambiguous function call syntax."

https://doc.rust-lang.org/reference/expressions/call-expr.html#disambiguating-function-calls

trait Frobber {
    fn frobnicate(&self) -> Self;
}

impl Frobber for i32 {
    fn frobnicate(&self) -> i32 {
        *self / 2
    }
}

// All of these print "Hello x    !"
fn main() {
    x::foo();
}

mod x {
    pub fn foo() {
        println!("{}", <i32 as super::Frobber>::frobnicate(&22));
    }
}

Playground link

Yep. I just learned that Syntax now :smile:

Thanks, guys!

1 Like

Those posts weren't there when I started composing mine. :man_shrugging:

1 Like