From 14f61c9776c6f4c71bee1cd7c21d4acfa0557c80 Mon Sep 17 00:00:00 2001 From: kjpgit Date: Thu, 29 Jan 2015 15:30:44 -0600 Subject: [PATCH 01/12] save --- text/0000-io-error-handling.md | 52 ++++++++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) create mode 100644 text/0000-io-error-handling.md diff --git a/text/0000-io-error-handling.md b/text/0000-io-error-handling.md new file mode 100644 index 00000000000..9ad1cb9812e --- /dev/null +++ b/text/0000-io-error-handling.md @@ -0,0 +1,52 @@ +- Start Date: 2015-01-29 +- RFC PR: (leave this empty) +- Rust Issue: (leave this empty) + +# Summary + +Decide the error handling policy for IO objects (Writer, BufferedWriter, etc.), +especially for "late" methods like flush and close. + + +# Motivation + +We want code to be correct and not let "errors pass silently". Find a +pragmatic solution to make the most code correct and obvious, and not have +hidden surprises. + +# Detailed design + +There should be a .close(&self) method of Writer that returns A Result<(), +IoError>. This also implicitly calls .flush(). + +IO objects that are dropped should not call .flush() or .close() on +themselves or any sub objects; only the minimal cleanup (freeing memory, +releasing file descriptors) should be performed. *drop means drop* + +Developers should be advised to call .close() of any IO objects and check the +result. If they don't, the remaining data won't get flushed and that should +get caught at dev time (a very good thing) + +Developers should be advised that panic / unwinding will not perform flush or +close, and that's probably a good thing (explicit is better than implicit). + +# Drawbacks + +Existing code breakage + +It's not the "simple python way", e.g. developers have to "type another line". + +# Alternatives + +Java like suppressed exceptions, RAII, double panics, ... + +I believe you either go all in and have destructors that can throw at any time +(including nested), or do not throw at all. Rust's architecture fits the +latter. + +# Unresolved questions + +Is consuming .close() technically and practically possible. You would lose the +.path() attribute if .close() returned an error (you could .clone() it before +hand though) + From 0ebe904fc9986d7d83dceec7564fc7f72632e68e Mon Sep 17 00:00:00 2001 From: kjpgit Date: Thu, 29 Jan 2015 15:38:23 -0600 Subject: [PATCH 02/12] save --- text/0000-io-error-handling.md | 28 ++++++++++++++++++---------- 1 file changed, 18 insertions(+), 10 deletions(-) diff --git a/text/0000-io-error-handling.md b/text/0000-io-error-handling.md index 9ad1cb9812e..c5608033586 100644 --- a/text/0000-io-error-handling.md +++ b/text/0000-io-error-handling.md @@ -5,30 +5,36 @@ # Summary Decide the error handling policy for IO objects (Writer, BufferedWriter, etc.), -especially for "late" methods like flush and close. +especially for "late" methods like flush and close. Not checking the return +code of close is a common but severe error. # Motivation -We want code to be correct and not let "errors pass silently". Find a -pragmatic solution to make the most code correct and obvious, and not have -hidden surprises. +We want code to be correct and not let "errors pass silently". But we also +don't want to worry about panics and double panics. Find a pragmatic solution +to make the most code correct and obvious, and not have hidden surprises. # Detailed design -There should be a .close(&self) method of Writer that returns A Result<(), -IoError>. This also implicitly calls .flush(). +There should be a `.close(self)` method of Writer that returns A `Result<(), +IoError>`. This also implicitly calls `.flush()`. If flush failed, the error +will be what was returned from flush, but resources are still released. -IO objects that are dropped should not call .flush() or .close() on +IO objects that are being dropped should not call `.flush()` or `.close()` on themselves or any sub objects; only the minimal cleanup (freeing memory, -releasing file descriptors) should be performed. *drop means drop* +releasing file descriptors) should be performed. *drop means drop it on the +floor* -Developers should be advised to call .close() of any IO objects and check the +Developers should be advised to call `.close()` of any IO objects and check the result. If they don't, the remaining data won't get flushed and that should get caught at dev time (a very good thing) Developers should be advised that panic / unwinding will not perform flush or close, and that's probably a good thing (explicit is better than implicit). +The rationale here is that a panic caused by array out of bounds may indicate +some severe error, and data should not be flushed (similar to the PoisonedMutex +philosophy). # Drawbacks @@ -46,7 +52,9 @@ latter. # Unresolved questions -Is consuming .close() technically and practically possible. You would lose the +Is consuming .close(self) technically and practically possible. You would lose the .path() attribute if .close() returned an error (you could .clone() it before hand though) +Does this work on what's returned from stdout() as well? I think so, its just a +BufferedWriter. However, .close() on that won't actually close the fd. From 61a436b6222e2ac818ce6c903f6995491a65d49c Mon Sep 17 00:00:00 2001 From: kjpgit Date: Thu, 29 Jan 2015 16:18:39 -0600 Subject: [PATCH 03/12] a few additional comments --- text/0000-io-error-handling.md | 33 ++++++++++++++++++++++++--------- 1 file changed, 24 insertions(+), 9 deletions(-) diff --git a/text/0000-io-error-handling.md b/text/0000-io-error-handling.md index c5608033586..bad54097015 100644 --- a/text/0000-io-error-handling.md +++ b/text/0000-io-error-handling.md @@ -13,28 +13,40 @@ code of close is a common but severe error. We want code to be correct and not let "errors pass silently". But we also don't want to worry about panics and double panics. Find a pragmatic solution -to make the most code correct and obvious, and not have hidden surprises. +to make the most code correct, fast, obvious, and predictable. # Detailed design There should be a `.close(self)` method of Writer that returns A `Result<(), IoError>`. This also implicitly calls `.flush()`. If flush failed, the error -will be what was returned from flush, but resources are still released. +will be what was returned from flush, but resources (in memory buffers, file +descriptors) are still released. -IO objects that are being dropped should not call `.flush()` or `.close()` on -themselves or any sub objects; only the minimal cleanup (freeing memory, -releasing file descriptors) should be performed. *drop means drop it on the -floor* +IO objects that are being dropped must not impicitly call `.flush()` or +`.close()` on themselves or any sub objects; only the minimal cleanup (freeing +memory, releasing file descriptors) should be performed. *drop means drop it +on the floor*. Developers should be advised to call `.close()` of any IO objects and check the result. If they don't, the remaining data won't get flushed and that should get caught at dev time (a very good thing) +The .close() method should probably set a flag indicating the file was properly +closed, so drop() doesn't try it again. In addition, some IO objects with +special constraints - i.e. designed for network file systems - might find it +useful to print a warning or even panic if they are being dropped and a panic +is not currently active but .close() was not called; that means the developer +failed to attempt .close() on them. + +Note that this RFC should not hurt the runtime speed of BufferedWriter, as +the 'was gracefully closed' flag is only checked by drop(), and .close(self) +consumes the object. + Developers should be advised that panic / unwinding will not perform flush or close, and that's probably a good thing (explicit is better than implicit). The rationale here is that a panic caused by array out of bounds may indicate -some severe error, and data should not be flushed (similar to the PoisonedMutex -philosophy). +some severe internal error, and additional data should not be flushed (similar +to the PoisonedMutex philosophy). # Drawbacks @@ -44,7 +56,10 @@ It's not the "simple python way", e.g. developers have to "type another line". # Alternatives -Java like suppressed exceptions, RAII, double panics, ... +Java like suppressed exceptions, RAII, double panics, do nothing... + +RAII works great for releasing existing resources that can't fail. +Flushing data out to disk is not "an existing resource". I believe you either go all in and have destructors that can throw at any time (including nested), or do not throw at all. Rust's architecture fits the From 1424c446633fd9e4f1a41a8635afdb4d55c897ca Mon Sep 17 00:00:00 2001 From: kjpgit Date: Thu, 29 Jan 2015 16:29:37 -0600 Subject: [PATCH 04/12] a few more notes --- text/0000-io-error-handling.md | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/text/0000-io-error-handling.md b/text/0000-io-error-handling.md index bad54097015..4adfe8c721a 100644 --- a/text/0000-io-error-handling.md +++ b/text/0000-io-error-handling.md @@ -15,6 +15,9 @@ We want code to be correct and not let "errors pass silently". But we also don't want to worry about panics and double panics. Find a pragmatic solution to make the most code correct, fast, obvious, and predictable. +Support use cases like compressed writers, encrypted writers, etc., which also +need end-to-end .close() checking and finalization. + # Detailed design There should be a `.close(self)` method of Writer that returns A `Result<(), @@ -50,7 +53,7 @@ to the PoisonedMutex philosophy). # Drawbacks -Existing code breakage +Existing code breakage (although said code is likely buggy) It's not the "simple python way", e.g. developers have to "type another line". @@ -61,6 +64,10 @@ Java like suppressed exceptions, RAII, double panics, do nothing... RAII works great for releasing existing resources that can't fail. Flushing data out to disk is not "an existing resource". +Suppressed exceptions in Java are useful for logging somewhere but it's hard to +see how actionable they are otherwise, and are not appropriate for a systems +language as it is basically an unbounded linked list. + I believe you either go all in and have destructors that can throw at any time (including nested), or do not throw at all. Rust's architecture fits the latter. From f9288b060d3043c37a1011cc233e707997a01caa Mon Sep 17 00:00:00 2001 From: kjpgit Date: Thu, 29 Jan 2015 16:35:51 -0600 Subject: [PATCH 05/12] add security concern --- text/0000-io-error-handling.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/text/0000-io-error-handling.md b/text/0000-io-error-handling.md index 4adfe8c721a..e4659fb784a 100644 --- a/text/0000-io-error-handling.md +++ b/text/0000-io-error-handling.md @@ -18,6 +18,10 @@ to make the most code correct, fast, obvious, and predictable. Support use cases like compressed writers, encrypted writers, etc., which also need end-to-end .close() checking and finalization. +Please also note that a "truncation attack" can be a severe security +vulnerability; having your /etc/passwd file only partially written is a DOS, as +is shortening a password. + # Detailed design There should be a `.close(self)` method of Writer that returns A `Result<(), From f2a3fe70a92e430f9dc2968fbe32c7ab7178e742 Mon Sep 17 00:00:00 2001 From: kjpgit Date: Thu, 29 Jan 2015 16:40:09 -0600 Subject: [PATCH 06/12] golang note --- text/0000-io-error-handling.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/text/0000-io-error-handling.md b/text/0000-io-error-handling.md index e4659fb784a..42ff9373618 100644 --- a/text/0000-io-error-handling.md +++ b/text/0000-io-error-handling.md @@ -68,6 +68,9 @@ Java like suppressed exceptions, RAII, double panics, do nothing... RAII works great for releasing existing resources that can't fail. Flushing data out to disk is not "an existing resource". +golang examples typically show `defer dst.Close()` (no error checking, not +useful) + Suppressed exceptions in Java are useful for logging somewhere but it's hard to see how actionable they are otherwise, and are not appropriate for a systems language as it is basically an unbounded linked list. From c39bad80ad007550cd3dd58c3e992f1908a90e4c Mon Sep 17 00:00:00 2001 From: kjpgit Date: Thu, 29 Jan 2015 16:47:22 -0600 Subject: [PATCH 07/12] add panic note spurred on by Diggsey --- text/0000-io-error-handling.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/text/0000-io-error-handling.md b/text/0000-io-error-handling.md index 42ff9373618..56a9a2b9419 100644 --- a/text/0000-io-error-handling.md +++ b/text/0000-io-error-handling.md @@ -43,7 +43,9 @@ closed, so drop() doesn't try it again. In addition, some IO objects with special constraints - i.e. designed for network file systems - might find it useful to print a warning or even panic if they are being dropped and a panic is not currently active but .close() was not called; that means the developer -failed to attempt .close() on them. +failed to attempt .close() on them. Note that this can give a false negative +if a file was entirely created and dropped in a destructor during a panic, but +I think that's acceptable. Note that this RFC should not hurt the runtime speed of BufferedWriter, as the 'was gracefully closed' flag is only checked by drop(), and .close(self) From 52bdffe020c06e5ab56ebcdb04d5bb768e643c80 Mon Sep 17 00:00:00 2001 From: kjpgit Date: Thu, 29 Jan 2015 21:50:55 -0600 Subject: [PATCH 08/12] add more rationale, exhaustive alternatives --- text/0000-io-error-handling.md | 88 ++++++++++++++++++++++++++-------- 1 file changed, 69 insertions(+), 19 deletions(-) diff --git a/text/0000-io-error-handling.md b/text/0000-io-error-handling.md index 56a9a2b9419..94b1d086446 100644 --- a/text/0000-io-error-handling.md +++ b/text/0000-io-error-handling.md @@ -4,13 +4,20 @@ # Summary -Decide the error handling policy for IO objects (Writer, BufferedWriter, etc.), -especially for "late" methods like flush and close. Not checking the return -code of close is a common but severe error. +Decide the error handling policy for IO objects that write to files (Writer, +BufferedWriter, etc.), especially for "late" methods like flush and close. +Not checking the return code of close is a common but severe error, especially +with async network filesystems. # Motivation +Writing to files safely presents special challenges. Unlike sockets, where +there is usually bidrectional communication, explicit ok responses, and +expectation of packet loss, writing to a file successfully is (unfortunately) +signaled by close(). Doing this in a destructor which can't throw (or +certainly not double throw), has been tried in C++ and is not safe. + We want code to be correct and not let "errors pass silently". But we also don't want to worry about panics and double panics. Find a pragmatic solution to make the most code correct, fast, obvious, and predictable. @@ -29,7 +36,7 @@ IoError>`. This also implicitly calls `.flush()`. If flush failed, the error will be what was returned from flush, but resources (in memory buffers, file descriptors) are still released. -IO objects that are being dropped must not impicitly call `.flush()` or +IO objects that are being dropped must not implicitly call `.flush()` or `.close()` on themselves or any sub objects; only the minimal cleanup (freeing memory, releasing file descriptors) should be performed. *drop means drop it on the floor*. @@ -39,23 +46,19 @@ result. If they don't, the remaining data won't get flushed and that should get caught at dev time (a very good thing) The .close() method should probably set a flag indicating the file was properly -closed, so drop() doesn't try it again. In addition, some IO objects with -special constraints - i.e. designed for network file systems - might find it -useful to print a warning or even panic if they are being dropped and a panic -is not currently active but .close() was not called; that means the developer -failed to attempt .close() on them. Note that this can give a false negative -if a file was entirely created and dropped in a destructor during a panic, but -I think that's acceptable. - -Note that this RFC should not hurt the runtime speed of BufferedWriter, as -the 'was gracefully closed' flag is only checked by drop(), and .close(self) +closed, so drop() doesn't try it again. + +Note that this RFC should not hurt the runtime speed of BufferedWriter, as the +'was gracefully closed' flag is only checked by drop(), because .close(self) consumes the object. Developers should be advised that panic / unwinding will not perform flush or -close, and that's probably a good thing (explicit is better than implicit). -The rationale here is that a panic caused by array out of bounds may indicate -some severe internal error, and additional data should not be flushed (similar -to the PoisonedMutex philosophy). +close, and that's probably a good thing (explicit is better than implicit). The +rationale here is that a panic caused by array out of bounds may indicate some +severe internal error, and additional data should not be flushed (similar to the +PoisonedMutex philosophy). Also, perhaps the system is out of memory and trying +to flush a giant buffer during unwinding would be counter productive. + # Drawbacks @@ -63,9 +66,37 @@ Existing code breakage (although said code is likely buggy) It's not the "simple python way", e.g. developers have to "type another line". +People that don't like it have to write a wrapper `UnsafeCloser` guard to +return to the current implicit, but not error checked, semantics. (Note that the +reverse is not possible today; I can't write a wrapper to undo an implicit close) + + # Alternatives -Java like suppressed exceptions, RAII, double panics, do nothing... +Add the `.close()` method but still fall back to flushing/closing with no error +checks in drop(), regardless of if a panic is happening. I think this tries to +predict intent and is not safe; there could be many bad reasons for the initial +panic and 'fail stop' could be more appropriate than flushing more buffers. It +also encourages not checking errors. Note: this approach is the current status quo. + +Add an UnsafeCloser guard in std:: to make the above more explicit opt-in. (Of +course that's probably as much work as just writing the .close() call) + +Add the `.close()` method but still fall back to flushing/closing with no error +checks in drop(), only if a panic was not happening. This will not cover the +case where a file is created and dropped entirely in a destructor during a +panic, and is hard to reason about - it increases state space. This also tries +to predict programmer intent; if `try!(myfile1.close())` failed, perhaps myfile2 +should not be automatically flushed either. + +Add the `.close()` method but still fall back to flushing/closing with panic +semantics in drop(), regardless of if a panic is happening. This seems +guaranteed to give double panics or needless panics, so is a non starter. (See +the bottom example of closing two files) + +Add the `.close()` method but still fall back to flushing/closing with panic +semantics in drop(), only if if a panic was not happening. Same problems as +previous (try! closing two files needlessly panicing). RAII works great for releasing existing resources that can't fail. Flushing data out to disk is not "an existing resource". @@ -89,3 +120,22 @@ hand though) Does this work on what's returned from stdout() as well? I think so, its just a BufferedWriter. However, .close() on that won't actually close the fd. + + +# Tried but Rejected + +I thought that files could warn or panic if they went out of scope and +`.close()` wasn't called. However, that could have many false positives. You +could start writing a file out and then discover your inputs were faulty, so +you'd just `return` early from a scope and the output file would be implicitly +dropped, and that should not indicate programmer error. + +A Closeable trait - where things are auto closed with panic semantics on +"normal" exit from scope is also quite difficult. Just consider: + + try!(my_file1.close()); + try!(my_file2.close()); + +If `my_file1.close()` errors, my_file2 will and should go out of scope implicitly +and not cause a panic. The programmer clearly intends that this code should not +panic. From a7ed1c1628e764320219c589446c783973e3740e Mon Sep 17 00:00:00 2001 From: kjpgit Date: Thu, 29 Jan 2015 22:07:23 -0600 Subject: [PATCH 09/12] add important note about line buffering. --- text/0000-io-error-handling.md | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/text/0000-io-error-handling.md b/text/0000-io-error-handling.md index 94b1d086446..6514dc53805 100644 --- a/text/0000-io-error-handling.md +++ b/text/0000-io-error-handling.md @@ -18,6 +18,11 @@ expectation of packet loss, writing to a file successfully is (unfortunately) signaled by close(). Doing this in a destructor which can't throw (or certainly not double throw), has been tried in C++ and is not safe. +IMPORTANT: The reason why rust "somewhat" works today is because Rust's stdout +is line buffered all the time. When that changes to be fully buffered, except +to tty, that means even `println!()` panic semantics are totally useless, because +nothing will be written until flush/close/drop. + We want code to be correct and not let "errors pass silently". But we also don't want to worry about panics and double panics. Find a pragmatic solution to make the most code correct, fast, obvious, and predictable. @@ -108,9 +113,6 @@ Suppressed exceptions in Java are useful for logging somewhere but it's hard to see how actionable they are otherwise, and are not appropriate for a systems language as it is basically an unbounded linked list. -I believe you either go all in and have destructors that can throw at any time -(including nested), or do not throw at all. Rust's architecture fits the -latter. # Unresolved questions From cbea5f3fc2c883bac7c733398d3705b732263287 Mon Sep 17 00:00:00 2001 From: kjpgit Date: Fri, 30 Jan 2015 08:18:17 -0600 Subject: [PATCH 10/12] split out rationale and add more of it --- text/0000-io-error-handling.md | 53 +++++++++++++++++++++------------- 1 file changed, 33 insertions(+), 20 deletions(-) diff --git a/text/0000-io-error-handling.md b/text/0000-io-error-handling.md index 6514dc53805..3882be42928 100644 --- a/text/0000-io-error-handling.md +++ b/text/0000-io-error-handling.md @@ -13,16 +13,11 @@ with async network filesystems. # Motivation Writing to files safely presents special challenges. Unlike sockets, where -there is usually bidrectional communication, explicit ok responses, and +there is usually bidirectional communication, explicit ok responses, and expectation of packet loss, writing to a file successfully is (unfortunately) signaled by close(). Doing this in a destructor which can't throw (or certainly not double throw), has been tried in C++ and is not safe. -IMPORTANT: The reason why rust "somewhat" works today is because Rust's stdout -is line buffered all the time. When that changes to be fully buffered, except -to tty, that means even `println!()` panic semantics are totally useless, because -nothing will be written until flush/close/drop. - We want code to be correct and not let "errors pass silently". But we also don't want to worry about panics and double panics. Find a pragmatic solution to make the most code correct, fast, obvious, and predictable. @@ -34,6 +29,12 @@ Please also note that a "truncation attack" can be a severe security vulnerability; having your /etc/passwd file only partially written is a DOS, as is shortening a password. +Rust "somewhat" works today because Rust's stdout is line buffered all the +time. When that changes to be fully buffered, except to tty, that means even +`println!()` panic semantics are totally useless, because nothing will be +written until flush/close/drop. + + # Detailed design There should be a `.close(self)` method of Writer that returns A `Result<(), @@ -48,21 +49,36 @@ on the floor*. Developers should be advised to call `.close()` of any IO objects and check the result. If they don't, the remaining data won't get flushed and that should -get caught at dev time (a very good thing) +get caught at dev time (a very good thing). -The .close() method should probably set a flag indicating the file was properly +The .close() method should set a flag indicating the file was properly closed, so drop() doesn't try it again. -Note that this RFC should not hurt the runtime speed of BufferedWriter, as the -'was gracefully closed' flag is only checked by drop(), because .close(self) -consumes the object. +# Rationale + +The reason `.close` consumes self is for two reasons. First, it doesn't hurt +the runtime speed of Writers, as the 'was closed' flag is only checked by +drop(). Second, close is not a retryable operation on posix, so this reflects +that. -Developers should be advised that panic / unwinding will not perform flush or -close, and that's probably a good thing (explicit is better than implicit). The -rationale here is that a panic caused by array out of bounds may indicate some -severe internal error, and additional data should not be flushed (similar to the -PoisonedMutex philosophy). Also, perhaps the system is out of memory and trying -to flush a giant buffer during unwinding would be counter productive. +One reason drop should not flush (due to unwinding or early return) is that +explicit is better than implicit. In some case, an early return may well mean +the programmers intent it *not* to flush other objects. In panic cases, due to +array out of bounds or out of memory, a programmer error may have happened or +trying to flush a giant buffer during unwinding would be counter productive. + +The other reason drop does not flush is it encourages correct error handling. +Developers know that HTTP requests and database transactions have to be checked +for success. Nobody in their right mind would make a destructor commit a +database transaction with no way to report failure. So it's mystifying why so +many think filesystems are somehow "more magical" and can't fail, and +destructors should try to "commit" filesystem actions by default. + +RAII works great for releasing existing resources that can't fail. Attempting +to flush buffers and metadata out to disk is not "an existing resource". + +Note that writer having write, flush, and close methods mirrors what Java does, +except close consumes the object. # Drawbacks @@ -103,9 +119,6 @@ Add the `.close()` method but still fall back to flushing/closing with panic semantics in drop(), only if if a panic was not happening. Same problems as previous (try! closing two files needlessly panicing). -RAII works great for releasing existing resources that can't fail. -Flushing data out to disk is not "an existing resource". - golang examples typically show `defer dst.Close()` (no error checking, not useful) From 2b1ce5b43bb5f30cdf50ace99d7df13b18c24946 Mon Sep 17 00:00:00 2001 From: kjpgit Date: Fri, 30 Jan 2015 08:27:11 -0600 Subject: [PATCH 11/12] fix grammar --- text/0000-io-error-handling.md | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/text/0000-io-error-handling.md b/text/0000-io-error-handling.md index 3882be42928..12283515c55 100644 --- a/text/0000-io-error-handling.md +++ b/text/0000-io-error-handling.md @@ -62,10 +62,11 @@ drop(). Second, close is not a retryable operation on posix, so this reflects that. One reason drop should not flush (due to unwinding or early return) is that -explicit is better than implicit. In some case, an early return may well mean -the programmers intent it *not* to flush other objects. In panic cases, due to -array out of bounds or out of memory, a programmer error may have happened or -trying to flush a giant buffer during unwinding would be counter productive. +explicit is better than implicit. An explicit early return due to an error may +well mean the programmers intent is *not* to flush other objects. In panic +cases, due to array out of bounds or out of memory, a programmer error may have +happened or trying to flush a giant buffer during unwinding would be counter +productive. The other reason drop does not flush is it encourages correct error handling. Developers know that HTTP requests and database transactions have to be checked From 8ca69fb8b795f5af3e06fb9b75d2fba47d30eb54 Mon Sep 17 00:00:00 2001 From: kjpgit Date: Tue, 3 Feb 2015 12:13:58 -0600 Subject: [PATCH 12/12] potential drawback of .close(self) --- text/0000-io-error-handling.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/text/0000-io-error-handling.md b/text/0000-io-error-handling.md index 12283515c55..e74991c1f48 100644 --- a/text/0000-io-error-handling.md +++ b/text/0000-io-error-handling.md @@ -84,10 +84,17 @@ except close consumes the object. # Drawbacks +Having `.close` consume `self` might be limiting composability - passing a +`&mut Writer` doesn't let the callee close it. Technically this is the case +today. + Existing code breakage (although said code is likely buggy) It's not the "simple python way", e.g. developers have to "type another line". +"My grandfather wrote his c++ code without calling .close() or checking errors, +so that's good enough for me" + People that don't like it have to write a wrapper `UnsafeCloser` guard to return to the current implicit, but not error checked, semantics. (Note that the reverse is not possible today; I can't write a wrapper to undo an implicit close)