Search code examples
rusttraitstype-aliasassociated-types

Ambiguous associated type when making a type alias for a numeric type


I'm writing a geometry library and am manipulating values that represent lengths. I've written a type alias for a type that represents lengths, currently set to f64 but could in the future be changed to f32 or possibly other numeric types (perhaps dependent on precision and space requirements):

type Length = f64;

All the functions and structs in this library would then be written in terms of this type, such as this function:

fn circumference(radius: Length) -> Length {
    Length::consts::PI * radius * radius
}

However, the function above causes the following compiler error:

   Compiling playground v0.0.1 (/playground)
error[E0223]: ambiguous associated type
 --> src/lib.rs:4:5
  |
4 |     Length::consts::PI * radius * radius
  |     ^^^^^^^^^^^^^^ help: use fully-qualified syntax: `<f64 as Trait>::consts`

error: aborting due to previous error

For more information about this error, try `rustc --explain E0223`.
error: could not compile `playground`

To learn more, run the command again with --verbose.

but doesn't give any elaboration about the Trait I should use — presumably I need to write <Length as Trait>::consts::PI for some appropriately chosen Trait, but it doesn't tell me what this trait should be.

Playground link

What trait should it be?


Solution

  • You can't access std::f64::consts::PI through the type alias Length. That PI is a module constant belonging to the consts submodule of std::f64. It isn't associated with the f64 primitive type.

    To access the module constant, you'll need to either use its fully qualified name (::std::f64::consts::PI) or import one of the modules in its path and use a partially qualified name. For example,

    use std::f64;
    fn circumference(radius: Length) -> Length {
        f64::consts::PI * radius * radius
    }
    
    // or
    
    use std::f64::consts as foo;
    fn circumference(radius: Length) -> Length {
        foo::PI * radius * radius
    }
    

    To avoid intermixing Length with its current definition, you can always define a local constant like

    const LENGTH_PI: Length = f64::consts::PI as Length;
    

    This way, if the type of Length changes in the future, you only have to replace f64::consts::PI once.