Conventions for checked teardown #61
Replies: 46 comments
-
We would need to call |
Beta Was this translation helpful? Give feedback.
-
Alternatively, the method can take |
Beta Was this translation helpful? Give feedback.
-
@kamalmarhubi |
Beta Was this translation helpful? Give feedback.
-
@opilar if |
Beta Was this translation helpful? Give feedback.
-
@dtolnay we discussed this last week and came to some conclusions but I can't remember what they were. I recall it seemed clear cut, but after reviewing this thread it doesn't seem so.
As with all things dtor seems like it's all bad tradeoffs. I think it's also reasonable to note that it's not obvious that retrying a failed close makes sense. Failing to close an I/O resource is almost always a nightmare, no-win scenario. I don't think I've ever seen a recommendation to retry closing a thing, and istm that an API that demanded close be retried would be extremely unfriendly. On some systems common wisdom is to assume close succeeds and just ignore failures to close. Having an explicit close in most cases seems primarily an opportunity to do some logging that something is really messed up. On the other hand in the tempdir case, closing means delete an entire directory tree, and we know that, especially on windows, that is a fraught operation. Still, we've even recently added defensive code in tempdir to do all kinds of things to try to make the delete 'just work', so it's hard to imagine what other heroics we might expect the caller to do to fix our failure to close. |
Beta Was this translation helpful? Give feedback.
-
I don't think it'd be a good idea to make a "law" in case different implementations might need to do different things. I actually like encoding in type whether operation is retry-able. The guidelines could be:
|
Beta Was this translation helpful? Give feedback.
-
@Kixunil @brson What to do with an error while an object goes out of scope? {
let a = A::new();
} // <-- error here |
Beta Was this translation helpful? Give feedback.
-
@opilar I think we have to preserve current convention - just ignoring the error. |
Beta Was this translation helpful? Give feedback.
-
@Kixunil I think ignoring the error is incorrect. |
Beta Was this translation helpful? Give feedback.
-
@opilar |
Beta Was this translation helpful? Give feedback.
-
@Kixunil You are right. For general case I think |
Beta Was this translation helpful? Give feedback.
-
I don't think
Obviously, |
Beta Was this translation helpful? Give feedback.
-
It can replace the file descriptor with -1 on Unix and the handle with
INVALID_HANDLE on Windows.
…On Tue, Jul 11, 2017 at 5:11 AM Martin Habovštiak ***@***.***> wrote:
I don't think close(&mut self) -> Result<(), E> is a good idea. Consider
this scenario:
- Thread 1 calls close()
- close fails, but OS deletes FD
- Thread 2 calls open(). The returned FD number is the same as
previously closed FD
- Thread 1 calls close() again to retry - FD opened by thread 2 is
closed
- Thread 2 has now invalid FD that was invalidated by other thread.
Obviously, close() must consume self in order to avoid this. This will
become even worse if thread 3 opens another file just after thread 1
retried: threads 2 and 3 will share same FD and likely corrupt data.
Finding out the cause would be a nightmare.
—
You are receiving this because you are subscribed to this thread.
Reply to this email directly, view it on GitHub
<https://github.com/brson/rust-api-guidelines/issues/61#issuecomment-314423930>,
or mute the thread
<https://github.com/notifications/unsubscribe-auth/ABY2URoP0n8ED_fGONx-sNRsgFI4xZyHks5sM2ZXgaJpZM4Naj6M>
.
|
Beta Was this translation helpful? Give feedback.
-
Yes, but that completely defeats the purpose of retrying and may introduce errors that could've been caught compile-time. I like Rust because precisely it can avoid such things. I'd even say that not consuming |
Beta Was this translation helpful? Give feedback.
-
The runtime errors are going to exist one way or another if the File/whatever lives in a struct. |
Beta Was this translation helpful? Give feedback.
-
@Kixunil I'm not sure I follow what you mean. As a recommendation for an API that libraries should follow, I'm in favour of If you're building an application and want to make your checked teardown take |
Beta Was this translation helpful? Give feedback.
-
What I mean is that if one takes by |
Beta Was this translation helpful? Give feedback.
-
@Kixunil I don't quite agree that an The kinds of bugs that are possible to result from I think we've exhausted the conversations around ergonomics and compile-time safety and will either have to accept the tradeoffs of one of the approaches or not recommend anything. If we can't recommend anything then I'd suggest simply leaving Do you have any ideas on how to move forward @dtolnay @alexcrichton? |
Beta Was this translation helpful? Give feedback.
-
@KodrAus It limits one how to handle accidental misuses. Taking by
It's not just that. Such design introduces another "invalid" state that must be handled in every method. This doesn't just slow down the code but puts burden on the implementor to write those checks (and not forget them). What you really want is
Maybe this is the best approach so far. |
Beta Was this translation helpful? Give feedback.
-
I prefer I'd like this for a standard: trait Close {
fn close(&self) -> Result<(), E>;
} Stuff will get dropped out of scope anyways. As for the example @alexcrichton gave with nested close method calls I believe this could be handled through the In my opinion the added complexity scenarios are more the responsibility of the individual developers rather than a concern for a general standard. Handling situations that come up as you go along is a very normal thing and using Is it just me or does anyone else mistakenly read close as clone all the time? |
Beta Was this translation helpful? Give feedback.
-
@danielpclark That's a very bad idea because it allows accidental use-after close. It also requires implementor to keep file name (url, whatever) stored too (allocated on the heap). |
Beta Was this translation helpful? Give feedback.
-
That's not necessarily the case. If the code is implemented in a way where something can be “used” only when open then closing it would end the allowed behavior until re-opened again.
I don't see that as a bad thing. Drop handles scope cleanup anyways. Here's How Ruby does Tempfile require 'tempfile'
file = Tempfile.new('foo')
file.path # => A unique filename in the OS's temp directory,
# e.g.: "/tmp/foo.24722.0"
# This filename contains 'foo' in its basename.
file.write("hello world")
file.rewind
file.read # => "hello world"
file.close
file.unlink # deletes the temp file Having the file name after was important in this use case. I imagine |
Beta Was this translation helpful? Give feedback.
-
@danielpclark the difference is that if you accidentally write something like this: let f = open("file")?;
f.read(&mut buf)?;
f.close()?;
// some code here
if(som_unlikely_condition()) {
f.read(&mut buf)?;
} In your case the code will panic or fail and that will be discovered only during testing. That takes time and it will require the programmer to analyze the situation to understand where the problem is. In my case it won't compile in the first place and the compiler will tell the programmer the exact line where the problem is. Ruby is not Rust - if it was, we wouldn't have Rust in the first place. Sure, there are cases when keeping the file name around is practical, but that should be the choice of the library user. |
Beta Was this translation helpful? Give feedback.
-
I think the compound close example could be improved with
The caller should be able to look into |
Beta Was this translation helpful? Give feedback.
-
Do we have examples of checked teardown - even if it is for APIs we don't have yet, or for APIs from other platforms - that are not
|
Beta Was this translation helpful? Give feedback.
-
POSIX.1-2008 does leave the state of a file descriptor after a failed So in terms of trait signatures, I would prefer |
Beta Was this translation helpful? Give feedback.
-
Named temporary files might be a good case to think about. |
Beta Was this translation helpful? Give feedback.
-
@zackw well said, thank you! |
Beta Was this translation helpful? Give feedback.
-
@zackw Nice examples, thanks. So, basically:
|
Beta Was this translation helpful? Give feedback.
-
The "unlink" equivalent for SysV shm is |
Beta Was this translation helpful? Give feedback.
-
We have a guideline:
Let's provide further guidance around naming the teardown method as well as how to decide whether it accepts
self
or&mut self
.Relevant tempdir issue: rust-lang-deprecated/tempdir#26
Beta Was this translation helpful? Give feedback.
All reactions