Trait bounds could include what they imply

I wanted to write a simple generic function to check if a value is zero. I had an idea - whatever cannot be converted to NonZero is zero. After a long struggle with the compiler, the code compiled, but it's nighly-only and the compiler still warns me about using an internal feature:

#![feature(nonzero_internals)]

use std::num::NonZero;
use std::num::ZeroablePrimitive;

fn is_zero<T>(value: T) -> bool
where
    T: TryInto<NonZero<T>> + ZeroablePrimitive,
{
    let new_value = TryInto::<NonZero<T>>::try_into(value);
    return new_value.is_err();
}

Try removing + ZeroablePrimitive, and the compiler spews a whole page of warnings. NonZero<T> is only implemented for T: ZeroablePrimitive.

I believe trait bounds could be smarter. If trait bounds imply other trait bounds, the latter should be added implicitly.

In this case, if NonZero<T> is used in trait bounds, it implies T: ZeroablePrimitive, so why should I write it explicitly?

Sounds like this RFC might hold the answers.

2 Likes

That's a struct implying its bounds, not a trait implying its bounds. But in either case...

SemVer implications -- and opting authors into such obligations involuntarily -- is a significant consideration. If you don't have to write it, the (or trait) author can no longer freely remove the requirement. Here's an article about it.

(My view is that the author should have the choice to opt in to the obligation or not.)

1 Like

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.