-
Notifications
You must be signed in to change notification settings - Fork 13.1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Change AsFd::as_fd
etc. from &self
to self
.
#93869
Conversation
Change the `AsFd` trait from: ```rust trait AsFd { fn as_fd(&self) -> BorrowedFd<'_>; } ``` to: ```rust trait AsFd<'a> { fn as_fd(self) -> BorrowedFd<'a>; } ``` and similar for `AsHandle` and `AsSocket`. Instead of implementing `AsFd` for `OwnedFd` etc., it's now implemented for `&'a OwnedFd` etc. This means that implementing `AsFd` changes from: ```rust impl AsFd for MyType { fn as_fd(&self) -> BorrowedFd<'_> { ... } } ``` to: ```rust impl<'a> AsFd<'a> for &'a MyType { fn as_fd(self) -> BorrowedFd<'a> { ... } } ``` Defining a function that uses an `AsFd` changes from: ```rust fn frob<Fd: AsFd>(fd: &Fd) ``` to: ```rust fn frob<'a, Fd: AsFd<'a>>(fd: Fd) ``` It also means that users of such functions can pass a `BorrowedFd` to them directly, instead of having to write an extra `&` to conceptually add an extra borrow around `BorrowedFd`. In total, it means somewhat more typing for implementors of the traits and implementors of functions that take `AsFd` parameters. But it also avoids a common confusion, and allows users to have fewer `&`s. And, it means fewer "reference to reference"-like situations. As an example of this in practice, the new unstable `fchown` function in std is [already taking an `AsFd` by value instead of by reference]. As written, it can't be passed a `&File`, and passing it a `File` consumes and closes the file. With this PR, it accepts `&File` and never consumes anything. One subtle detail is that the Unix internal `File`, `Socket`, and `AnonPipe` types need to stop implementing `AsFd`, so that they don't require stability attributes which are otherwise apparently required. Since these are internal types, this doesn't affect the public API. There are comments in the code for these. Another subtle detail is that with the convention of types implementing `AsFd` for `&'a T`, they don't implement it for `&'a mut T`. For example, with the `frob` function above, `frob(&mut file)` gets this error: ``` error[E0277]: the trait bound `&mut File: AsFd<'_>` is not satisfied ``` One needs to use `frob(&file)` instead. If one has a `mut T`, it's just a matter of typing `&` instead of `&mut`, but if one has a `&mut T`, one needs to use `&*`. This is a little awkward, however the only alternative I know about is to oblige all types that implement `AsFd<'a>` for `&'a T` to also implement `AsFd<'a>` for `&'a mut T`, which seems worse. [already taking an `AsFd` by value instead of by reference]: https://github.com/rust-lang/rust/blob/734368a200904ef9c21db86c595dc04263c87be0/library/std/src/os/unix/fs.rs#L974
r? @yaahc (rust-highfive has picked a reviewer for you, use r? to override) |
This comment has been minimized.
This comment has been minimized.
Hmmm, interesting. And good catch regarding If this were just more typing for people implementing But the substantial added complexity for people writing functions that take an Is there no other way we could fix this to be more usable, and accept borrowed values, without introducing explicit lifetimes everywhere? Is there some way we could make this feel closer to |
Ooh, I think I just figured it out. What would you think if std added this, instead of this PR: impl<T: AsFd> AsFd for &T {
fn as_fd(&self) -> BorrowedFd<'_> {
T::as_fd(self)
}
}
impl<T: AsFd> AsFd for &mut T {
fn as_fd(&self) -> BorrowedFd<'_> {
T::as_fd(self)
}
} Everything else would stay the same. With just this change, It wouldn't stop users from passing an owned |
Add implementations of `AsFd` for `&T` and `&mut T`, so that users can write code like this: ```rust pub fn fchown<F: AsFd>(fd: F, uid: Option<u32>, gid: Option<u32>) -> io::Result<()> { ``` with `fd: F` rather than `fd: &F`. And similar for `AsHandle` and `AsSocket` on Windows. Also, adjust the `fchown` example to pass the file by reference. The code can work either way now, but passing by reference is more likely to be what users will want to do. This is an alternative to rust-lang#93869, and is a simpler way to achieve the same goals: users don't need to pass borrowed-`BorrowedFd` arguments, and it prevents a pitfall in the case where users write `fd: F` instead of `fd: &F`.
I've now submitted #93888 which implements this new approach. It's much simpler; thanks for pushing me in this direction! |
#93888 is working out well in my own tests, so I'll close this in favor of it. |
Glad the alternative worked out! |
…or-ref, r=joshtriplett Implement `AsFd` for `&T` and `&mut T`. Add implementations of `AsFd` for `&T` and `&mut T`, so that users can write code like this: ```rust pub fn fchown<F: AsFd>(fd: F, uid: Option<u32>, gid: Option<u32>) -> io::Result<()> { ``` with `fd: F` rather than `fd: &F`. And similar for `AsHandle` and `AsSocket` on Windows. Also, adjust the `fchown` example to pass the file by reference. The code can work either way now, but passing by reference is more likely to be what users will want to do. This is an alternative to rust-lang#93869, and is a simpler way to achieve the same goals: users don't need to pass borrowed-`BorrowedFd` arguments, and it prevents a pitfall in the case where users write `fd: F` instead of `fd: &F`. r? `@joshtriplett`
…or-ref, r=joshtriplett Implement `AsFd` for `&T` and `&mut T`. Add implementations of `AsFd` for `&T` and `&mut T`, so that users can write code like this: ```rust pub fn fchown<F: AsFd>(fd: F, uid: Option<u32>, gid: Option<u32>) -> io::Result<()> { ``` with `fd: F` rather than `fd: &F`. And similar for `AsHandle` and `AsSocket` on Windows. Also, adjust the `fchown` example to pass the file by reference. The code can work either way now, but passing by reference is more likely to be what users will want to do. This is an alternative to rust-lang#93869, and is a simpler way to achieve the same goals: users don't need to pass borrowed-`BorrowedFd` arguments, and it prevents a pitfall in the case where users write `fd: F` instead of `fd: &F`. r? ``@joshtriplett``
…or-ref, r=joshtriplett Implement `AsFd` for `&T` and `&mut T`. Add implementations of `AsFd` for `&T` and `&mut T`, so that users can write code like this: ```rust pub fn fchown<F: AsFd>(fd: F, uid: Option<u32>, gid: Option<u32>) -> io::Result<()> { ``` with `fd: F` rather than `fd: &F`. And similar for `AsHandle` and `AsSocket` on Windows. Also, adjust the `fchown` example to pass the file by reference. The code can work either way now, but passing by reference is more likely to be what users will want to do. This is an alternative to rust-lang#93869, and is a simpler way to achieve the same goals: users don't need to pass borrowed-`BorrowedFd` arguments, and it prevents a pitfall in the case where users write `fd: F` instead of `fd: &F`. r? ```@joshtriplett```
Change the
AsFd
trait from:to:
and similar for
AsHandle
andAsSocket
. Instead of implementingAsFd
for
OwnedFd
etc., it's now implemented for&'a OwnedFd
etc.This means that implementing
AsFd
changes from:to:
Defining a function that uses an
AsFd
changes from:to:
It also means that users of such functions can pass a
BorrowedFd
to themdirectly, instead of having to write an extra
&
to conceptually add anextra borrow around
BorrowedFd
.In total, it means somewhat more typing for implementors of the traits and
implementors of functions that take
AsFd
parameters. But it alsoavoids a common confusion, and allows users to have fewer
&
s. And, itmeans fewer "reference to reference"-like situations.
As an example of this in practice, the new unstable
fchown
function in stdis already taking an
AsFd
by value instead of by reference. Aswritten, it can't be passed a
&File
, and passing it aFile
consumesand closes the file. With this PR, it accepts
&File
and never consumesanything.
One subtle detail is that the Unix internal
File
,Socket
, andAnonPipe
types need to stop implementingAsFd
, so that they don'trequire stability attributes which are otherwise apparently required.
Since these are internal types, this doesn't affect the public API.
There are comments in the code for these.
Another subtle detail is that with the convention of types implementing
AsFd
for&'a T
, they don't implement it for&'a mut T
. For example,with the
frob
function above,frob(&mut file)
gets this error:One needs to use
frob(&file)
instead. If one has amut T
, it's justa matter of typing
&
instead of&mut
, but if one has a&mut T
, oneneeds to use
&*
. This is a little awkward, however the only alternativeI know about is to oblige all types that implement
AsFd<'a>
for&'a T
to also implement
AsFd<'a>
for&'a mut T
, which seems worse.