Implement `std::os::fd::AsRawFd` for references

It'd be nice to add implementation of AsRawFd for references &T and &mut T where T: AsRawFd.

impl<T: AsRawFd> AsRawFd for &T { /*...*/ }
impl<T: AsRawFd> AsRawFd for &mut T { /*...*/ }

Modification is super easy, and it would improve the ergonomic of using the trait.

We have both of those impls for AsFd, which is the safe replacement for AsRawFd.

We could certainly add the same impls for AsRawFd, if needed, but new code should be trying to avoid using AsRawFd wherever possible.

My first thought was that AsFd doesn't fit into my use-case, but actually it does. Thanks for the tip!

2 Likes

Not sure though if AsFd can fully replace AsRawFd so the latter doesn't need to be improved. On other hand, I can't think of any use-case where AsFd doesn't work...

I don't think we can -- it's a breaking change to add new blanket impls on a fundamental type.

7 Likes

Oh, you're right. Technically someone could have conflicting impls.

In any case, given that any new code should be using AsFd instead, probably not worth adding in any case even if we could.

1 Like

How would I be able to identify that AsRawFd is a fundamental type using source code or documentation? Are the traits in std classified as fundamental?

There's nothing special about AsRawFd here, the special thing is references, &T. Those are considered "fundamental", which means you can do things with them you can't with other types, such as impl ExternalTrait for &MyType. (By contrast, you can't do impl ExernalTrait for Vec<MyType> or impl ExternalTrait for Option<MyType>, for instance.)

This has unfortunately been forgotten often -- Extend not being implemented on &mut is my personal sadness example, and adding it was rejected because it's breaking.

It could be implemented for &mut T in new edition, it wouldn't introduce a breaking change in this way. They've recently launched a call of proposals for the next edition, maybe that could be suggested

Editions don't help; crates that implement it for their own &mut MyType couldn't interoperate with the std having a blanket implementation.

1 Like

Crates would have to be manually upgraded if they wish to switch edition to a newer one. If a crate doesn't change edition, nothing changes for it.

Crates of different editions have to interoperate, every statement or expression or so in Rust has it's own edition due to things like macros, coherence is edition agnostic, updating to a new edition isn't supposed to be a breaking change, etc.

Limits of edition changes

[ ...]

Some examples of changes editions cannot make:

  • Loosening the coherence rules in just one edition.
    • Coherence is a "zero sum" global property that states that there are no two impls that apply to the same types. This property must hold across all crates, regardless of edition, so we can't tweak the rules in just crates in the 2021 Edition to permit impls that would be disallowed in 2018 crates. If we did so, then crates in the 2018 Edition could add the impl under the old rules, and another crate in the 2021 Edition could add the same impl per the new rules, resulting in an error.
4 Likes

So impl &MyType is allowed because &MyType is fundamental, meaning it's a reference to a generally undefined generic type, whereas impl Vec<MyType> is not allowed because Vec is its self built-in to the standard library, which is special, and modifying how it handles fundamental (generic references) would break coherence.