-
-
Notifications
You must be signed in to change notification settings - Fork 14.2k
Change AsFd::as_fd etc. from &self to self.
#93869
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
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
AsFdtrait from:to:
and similar for
AsHandleandAsSocket. Instead of implementingAsFdfor
OwnedFdetc., it's now implemented for&'a OwnedFdetc.This means that implementing
AsFdchanges from:to:
Defining a function that uses an
AsFdchanges from:to:
It also means that users of such functions can pass a
BorrowedFdto 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
AsFdparameters. 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
fchownfunction in stdis already taking an
AsFdby value instead of by reference. Aswritten, it can't be passed a
&File, and passing it aFileconsumesand closes the file. With this PR, it accepts
&Fileand never consumesanything.
One subtle detail is that the Unix internal
File,Socket, andAnonPipetypes 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
AsFdfor&'a T, they don't implement it for&'a mut T. For example,with the
frobfunction 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 Tto also implement
AsFd<'a>for&'a mut T, which seems worse.