-
Notifications
You must be signed in to change notification settings - Fork 1.6k
Add file-open-with RFC #2615
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
Add file-open-with RFC #2615
Conversation
|
||
let mut file = File::open_with("foo.txt", OpenOptions::new().read().write()); | ||
|
||
This matches the normal way of doing "call a method with some options" in Rust, for example `TcpStream::connect(addr)` and `TcpStream::connect_timeout(addr, timeout)`. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The current pattern, however, matches the well-known and frequently used builder pattern. Since you have to use OpenOptions
anyways, I don't really see how this improves on just calling .open
on that.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The improvement is discoverability and consistency. There's already File::open()
and File::create()
which leads users to believe that to open files you use a method on File
.
If OpenOptions
had been named FileOpenBuilder
or something then I agree it would have been a bit clearer but given that it isn't, and that the most obvious methods to open a file are on File
it seems silly that some random subset of file open methods are on OpenOptions
.
This may be one of those things that feels obvious once you have learnt it, but as a Rust beginner, trust me this was weird and confusing. (And see the Stackoverflow questions for further evidence.)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Perhaps then the docs on File::{open, create}
should be improved to mention that you need to use OpenOptions
to configure permissions
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That would certainly be a good thing to do, however it would be better if the API were intuitive in the first place. Nobody likes reading documentation.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The discoverability of OpenOptions
definitely has room for improvement. I recall being a little confused when I first needed it and saw nothing obvious associated with File
.
As an alternative, we could add an open_options
constructor to make it easier to find while staying true to the builder pattern.
From your example:
let mut file = OpenOptions::new()
.read(true)
.write(true)
.open("foo.txt");
Would also be available via:
let mut file = File::open_options()
.read(true)
.write(true)
.open("foo.txt");
And implemented:
impl File {
pub fn open_options() -> OpenOptions {
OptionOptions::new()
}
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For the specific use case of opening a file, I don't think the builder pattern makes much intuitive sense. After all, you're not building a file. You're building an OpenOptions
object. Or perhaps you could say you're building a file descriptor. But it seems weird to say that the file itself has been "built" before we write any content into it. Maybe it's possible to design a useful FileBuilder
API where .build()
actually puts content in the file, but that'd be a layer above what std
is trying to provide.
I think it's clear that File::open(name, options)
is a much more intuitive API. To me the only question is whether adding it and deprecating the already stable alternative is a net win for the language and its ecosystem, which is where I'll have to defer to others who know the Rust ecosystem much better.
|
||
``` | ||
impl File { | ||
pub fn open_with(filename: &str, options: &OpenOptions) -> File { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This will need to take the same AsRef<Path>
type parameter as File::open.
https://doc.rust-lang.org/std/fs/struct.File.html#method.open
I like @aloucks's alternative better - it solves the discoverability issue, and improves on both open_with and the status quo by not requiring you to import OpenOptions at all. |
RFCs aren't normally required for small API additions like this, BTW. You can just make a PR. |
Ah ok. I think @aloucks's suggestion is an improvement too. I'll make a PR for it since it seems to have the most support. |
// bikeshedding Another option would be to call the constructor method impl File {
pub fn with_options() -> OpenOptions {
Default::default()
}
}
...
File::with_options().write(true).open("foo.txt"); The tiny difference being -- that it reads much more naturally like a proper English sentence. As in, "With options this, this, this, and that, open foo.txt". |
Closing in favour of #65429. |
Add File::with_options This provides a more fluent API to create files with options, and also avoids the need to import OpenOptions. This implements @aldanor's [suggestion](rust-lang/rfcs#2615 (comment)) which was popular.
Rendered
This RFC proposes making
File::open()
consistent by deprecatingOpenOptions::new().read(true).write(true).open("existing_file")
and addingFile::open_with("existing_file", OpenOptions::new().read().write())
instead.Suggestion based on rust-lang/rust#55762.