Skip to content
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

Proposal: io/ioutil: convert Discard from io.Writer to io.WriteCloser #22357

Closed
karalabe opened this issue Oct 20, 2017 · 6 comments
Closed

Proposal: io/ioutil: convert Discard from io.Writer to io.WriteCloser #22357

karalabe opened this issue Oct 20, 2017 · 6 comments

Comments

@karalabe
Copy link
Contributor

The ioutil package has a nice predefined utility type called Discard, which:

Discard is an io.Writer on which all Write calls succeed without doing anything.

This however cannot be used in scenarios where we need an io.WriteCloser. My question would be if it would make sense for ioutil.Discard to implement io.Closer too, so it could be used for scenarios where we only need a noop Close method.

Note, this change would remain backwards compatible, as any code relying on ioutil.Discard being an io.Writer should still work if it also becomes an io.Closer.


The debatable aspect of this proposal is what the Close method should return, and whether Write should or shouldn't not work after a close being called. I.e. I'm aware this proposal might be rejected on these grounds, but hoping for a discussion to make it clear why io.Writer and not io.WriteCloser.

@gopherbot gopherbot added this to the Proposal milestone Oct 20, 2017
@mvdan
Copy link
Member

mvdan commented Oct 20, 2017

hoping for a discussion to make it clear why io.Writer and not io.WriteCloser

I would say because the former is enough in almost every scenario. And it's not clear what Close should do, as you say.

If Close does nothing, it would be trivial to wrap Discard in something similar to ioutil.NopCloser. And if Close did do something, I would assume the change would be too drastic to fit in Go1.

Note that whatever the change made is, this could still break programs. Some code may detect if a Writer is a WriteCloser via reflection, for example.

@odeke-em odeke-em changed the title Proposal: convert ioutil.Discard from io.Writer to io.WriteCloser Proposal: io/ioutil: convert Discard from io.Writer to io.WriteCloser Oct 20, 2017
@ianlancetaylor
Copy link
Contributor

Technically changing the type of ioutil.Discard would break the Go 1 compatibility guarantee. I admit it's hard to imagine that it would break any real program, but that is still a clear drawback, and we need a significant benefit to make such a change. I don't see the significant benefit here; I doubt this comes up all that often, and when it does it's easy enough to write

type DiscardCloser struct {
    ioutil.Discard
}
func (DiscardCloser) Close() error { return nil }

@mrjoshuak
Copy link

I find that I need a no op closer as often as I need a no op writer. File io patterns are very common.

Just my opinion mind you, but personally "enough in almost every scenario" is never a compelling argument to me even if it were based on a statistical reality. It's just the same as saying it's almost enough. Which is also to say that it isn't.

Further I can't imagine any reasonable logic which isn't already broken that would take a different and conflicting action based on an abstract type bing a WriteCloser and not a Writer. A WriteCloser is by definition also a Writer, while the converse is not. More generally is it not within the language definition that it is 'safe', if not encouraged, to use types with more expansive interfaces than strictly required in a particular context?

Checking if an io.Writer is actually an io.WriteCloser seems like the breaking behavior to me. If I give an io.Writer to a method that calls for an io.Writer, it had damn well not check to see if I have a Close method on the object and do something different based on that information; like, heaven forbid, call Close(). If you ask for an io.Writer you are claiming an io.Writer is all you need.

The real value judgement is not one of convenience vs go 1 breaking change. I don't see any breaking change, and more than convenience the change leads to cleaner more readable code. Having to introduce new, effectively meaningless, types into code substantially reduces readability even in simple cases like this. If one coder fails to name these types something like DiscardCloser, then a reviewer has to go find this type perhaps in another file, just to discover that it doesn't do anything at all.

Just an opposing opinion to the opposing opinions, take it for whatever it's worth.

@ianlancetaylor
Copy link
Contributor

An example of the way this would be a breaking change is

    d := ioutil.Discard
    var p *ioutil.Writer = &d

That compiles today but would not compile if we made this change.

@rsc
Copy link
Contributor

rsc commented Nov 20, 2017

To be clear, the definition of Discard is:

var Discard io.Writer = devNull(0)

We cannot change the declared type here, as Ian noted. A better answer might be a NopCloserWriter adapter like NopCloser is for Reader. Then you can use NopCloserWriter(Discard).

I guess my other question is

This however cannot be used in scenarios where we need an io.WriteCloser.

Why do you need an io.WriteCloser?

@rsc
Copy link
Contributor

rsc commented Nov 20, 2017

@robpike filed #22823 for NopWriteCloser (or whatever spelling is right). I think we can close this issue, since we clearly can't change the type of Discard.

@rsc rsc closed this as completed Nov 20, 2017
@golang golang locked and limited conversation to collaborators Nov 20, 2018
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests

6 participants