How can I unambiguously refer to an inherent method?

The following code triggers the lint:

#![no_std]

trait Abs {
    fn abs(self) -> Self;
}

impl Abs for f64 {
    fn abs(self) -> f64 {
        core::primitive::f64::abs(self)
    }
}

The warning is

warning: function cannot return without recursing
 --> src/lib.rs:8:5
  |
8 |     fn abs(self) -> f64 {
  |     ^^^^^^^^^^^^^^^^^^^ cannot return without recursing
9 |         core::primitive::f64::abs(self)
  |         ------------------------------- recursive call site
  |
  = note: `#[warn(unconditional_recursion)]` on by default
  = help: a `loop` may express intention better if this is on purpose

This recurses on no_std because f64 does not have a dedicated .abs() method.

How can I unambiguously refer to the dedicated method, such that the above implementation will fail to compile on no_std?

Usually you'd use fully qualified syntax to unambiguously refer to a method, but that doesn't seem to be helpful in this case since it doesn't provide a way to choose the inherent method.

You can still make the above fail to compile, but I don't think you can do it by unambiguously referring to the inherent method:

impl Abs for f64 {
    #[deny(unconditional_recursion)]
    fn abs(self) -> f64 {
        f64::abs(self)
    }
}

Inherent methods are preferred over trait methods in name lookup, so there's no disambiguator to pick them.

Can you elaborate on the situation where you want to write this in a library without knowing whether said library is no_std?

1 Like

One hacky solution is to put the call to the inherent f64::abs into a module where the Abs trait is not in scope:

pub trait Abs {
    fn abs(self) -> Self;
}

impl Abs for f64 {
    fn abs(self) -> f64 {
        inner::abs(self)
    }
}

mod inner {
    pub fn abs(x: f64) -> f64 { x.abs() }
}
2 Likes

How about starting the path with std:: instead of core::?

2 Likes

I thought I tried that... std::primitive::f64::abs(self) was so simple :laughing:

Thanks for the replies everyone. To elaborate, I have a macro that generates this implementation for all numeric types. I ended up having broken code (it recursed for f32 and f64) when I made my crate support no_std so I was wondering how in the future I could write the code such that it would have failed to compile instead of just warned.

I'll probably use #[deny(unconditional_recursion)] for now.