5 ms·
let wordlist_file = File::open("wordlist.txt")?; // do something... // we don't need to close wordlist_file // it will be closed when the variable g
by hu3 5y ago
let wordlist_file = File::open("wordlist.txt")?;
// do something...
// we don't need to close wordlist_file
// it will be closed when the variable goes out of scope
What happens if there is an error when closing the file that I have written to?
- LegionMammal978 5y agoThe error will be ignored, per the docs for File [0]: > Files are automatically closed when they go out of scope. Errors detected on closing are ignored by the implementation of Drop. Use the method sync_all if these errors must be manually handled. [0] https://doc.rust-lang.org/stable/std/fs/struct.File.html https://doc.rust-lang.org/stable/std/fs/struct.File.html
- josephg 5y agoThat sounds like a weird choice. When do you ever write to a file but not care if the write has failed? Sounds like a source of bugs any time your file system isn’t 100% reliable.
- eptcyka 5y agoIf you close your files without waiting for fsync to return first, do you really care if the data has hit the disk? If fsync didn't fail, but close fails, what can you do then? Calling close() doesn't imply anything about flushing buffers or syncing data to disk or anything like that. It's just a signal to the OS that your process is done with this particular resource.
- CraigJPerry 5y agoYou should probably notify the user though
- eptcyka 5y agoIt depends. Regardless, it seems like Go will close file handles for you too, if you've not done it yet, in the finalizer code.
- ilammy 5y agoAlso, even if close() fails, you can no longer use the file descriptor anyway, since it might be already reused. The only thing you should do with the error from close() is log it to diagnose why it happened and prevent it next time.
- deathanatos 5y agoI agree it's a bit unfortunate. The rub here would be that `Drop` would become fallible, and if it is fallible, then … how does it fail, exactly? (What happens to the error?) There's exceptions, but the downsides to such systems are pretty extensively covered. Nonetheless, the point here is that RAII offers a deterministic close compared to other approaches, at least, even if the write's success isn't covered. You can get that, too, with, wordlist_file.flush()?; or wordlist_file.sync_all()?; depending on desires. (And again, I agree that requiring the programmer to remember code in order to obtain safe behavior is not desirable. But this problem manifests in pretty much any other language, and typically in worse manners.)
- deleted 5y ago[deleted]
- ben0x539 5y agoYeah, it's not an ideal situation. A lot of the time you just read from the file and then you don't really care about the success of close(), though. Maybe that should be a whole set of different types...
- deleted 5y ago[deleted]
- SCHiM 5y agoOff topic: what type of error can occur when closing a file? Is it somehow possible that the kernel denies your request, and forces your handle to stay open?
- TheDong 5y agoQuoting from "man 2 close": https://man7.org/linux/man-pages/man2/close.2.html https://man7.org/linux/man-pages/man2/close.2.html > it is quite possible that errors on a previous write(2) operation are reported only on the final close() ... Failing to check the return value when closing a file may lead to silent loss of data. > the behavior that occurs on Linux ... the file descriptor is guaranteed to be closed. So yeah, it's always closed on linux, but POSIX doesn't guarantee that for EINTR specifically, and there are sometimes meaningful errors.
- lilyball 5y agoFWIW Rust automatically retries the operation on EINTR.
- dllthomas 5y ago> Failing to check the return value when closing a file may lead to silent loss of data. Checking, on the other hand, probably makes it loud loss of data.
- turminal 5y agoWhy would it? If you care enough to check you probably also care enough not to discard the data you attempted to write.
- dllthomas 5y agoI was referring to checking because "you should check" when you don't actually care enough to structure your program around not losing the data. It's easy to to check and emit a diagnostic, it's harder to make sure you have collected the data you've written to a file descriptor and have something sensible to do with it if the close fails.
- deleted 5y ago[deleted]
- verdagon 5y agoAs far as I know, Vale is the only language that can statically ensure we handle that error with its Higher RAII [0], a form of linear typing. Basically, File's drop() returns a Result, and the compiler enforces that we use it. I hear linear types might also be coming to Haskell soon, which is pretty exciting. Such a thing is unfortunately impossible in Rust (though many languages can detect it at run-time). [0] https://verdagon.dev/blog/higher-raii-7drl https://verdagon.dev/blog/higher-raii-7drl
- kitkat_new 5y agowhy is it impossible?
- verdagon 5y agoIn Rust, drop() can't return anything, it must return void.
- zozbot234 5y agoBecause panics in Rust can force-drop data as part of unwinding the stack. If Rust supported panic-free code easily, it could also add linear data which would eliminate the default Drop and replace it with custom, user-managed operations.
- Rusky 5y agoThere's just not a language feature to prevent you from letting an object go out of scope without "using" it. There is `Drop`, which automatically runs when an object goes out of scope, but you can skip that by leaking the object, e.g. with an Rc cycle. There is also `#[must_use]`, which warns when you don't do anything with an object, but you can explicitly silence that with `let _ = the_object` or `let _bla = the_object`, and it doesn't complain if you exit that scope early (either via normal control flow or panic). The reason Rust has `Drop` and `#[must_use]` but not linear types (short of "nobody's bothered yet") is that they aren't really all that pleasant to use and they impose a lot of constraints on the APIs you build with them. You would have to prevent leaks of linear objects (which means, among other things, no reference counting or moving between threads!). You would have to track panics in the type system and/or forbid calling most functions while a linear object is live. More detail here: https://gankra.github.io/blah/linear-rust/ https://gankra.github.io/blah/linear-rust/ The current design means the default way to use a `File` won't handle errors on close, but you can opt into that by making an explicit call yourself. In exchange, you can do a lot of things with `File` objects that you couldn't with a linear `File`. (My suspicion is that Vale either doesn't use linear types for files, doesn't actually enforce linearity at compile time, or makes files painful to use with generic code.)