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: allow main.main to optionally return an error #27328

Closed
nigeltao opened this issue Aug 29, 2018 · 10 comments
Closed

proposal: allow main.main to optionally return an error #27328

nigeltao opened this issue Aug 29, 2018 · 10 comments

Comments

@nigeltao
Copy link
Contributor

The spec says that "The main package must have package name main and declare a function main that takes no arguments and returns no value [emphasis added]."

I propose to additionally allow returning error. Combined with the check and default-handler mechanisms suggested in the Error Handling Go 2 Draft Design, this could reduce some boilerplate. It would be equivalent to:

// main is the entry point.
func main() {
	if err := main1(); err != nil {
		os.Stderr.WriteString(err.Error() + "\n")
		os.Exit(1)
	}
}

// main1 is what the programmer writes as "func main() error".
func main1() error {
	// etc
}

This is admittedly sugar, but these days, I write that trivial main-calls-main1 dance in most of my package mains. For example:

To be clear, func main() { etc } that returned no value would still be valid, and have unchanged semantics.

This is similar to issue #21111 but this one is for main functions and that one is for testing functions (including test examples, which are somewhat similar to main programs).

@nigeltao
Copy link
Contributor Author

Indeed, the Error Handling overview says:


It would be shorter and clearer to write instead:

func main() {
	handle err {
		log.Fatal(err)
	}

	hex := check ioutil.ReadAll(os.Stdin)
	data := check parseHexdump(string(hex))
	os.Stdout.Write(data)
}

It would be even shorter, and I think even clearer, if func main() was func main() error and we dropped the explicit handle block:

func main() error {
	hex := check ioutil.ReadAll(os.Stdin)
	data := check parseHexdump(string(hex))
	os.Stdout.Write(data)
}

@earthboundkid
Copy link
Contributor

I too often end up writing three line main functions that call another function which returns an error, but I think if handle blocks were accepted then I could just put that code into the handle block and not use the wrapper, so this wouldn't be as helpful.

Anyway, if main returns a value, why not have it be an int for the error code? And why not have main take a slice of os.Args? Those are both common in other languages, so I assume it was omitted for a reason.

@cherrymui
Copy link
Member

I'm not sure I understand correctly, but would it be addressed if we special-case main.main's default handler? That is, for main.main (and only main.main), the default handler is

	handle err {
		log.Fatal(err) // or any other form of print+exit
	}

I think even if we allow main.main return an error, there still needs a default/predefined/magical wrapper of main.main that does the log.Fatal or os.Exit thing? If there will be one of such thing anyway, would it just be main.main's default handler?

@nigeltao
Copy link
Contributor Author

It could just be a default handler for main.main, I suppose, although my instincts are that a default handler seems more special-case-y than letting it return an error. OTOH it wouldn't need to change any tools that assumed that main.main returned nothing.

Or I could just keep writing the explicit code that I already do. It's slightly simpler with the draft-suggestion handle mechanism, as I wouldn't need the main / main1 split.

@nigeltao
Copy link
Contributor Author

Similar suggestions are to let init functions also optionally return error and also somehow let global variable initializations use check. With hindsight, that would have eliminated the need for Must-y functions like template.Must and regexp.MustCompile.

@nigeltao
Copy link
Contributor Author

why not have it be an int for the error code?

Well, Go's error type is richer than ints. It can be an arbitrary error message, e.g. including the filename of things that failed.

I'm also not sure if "programs return int" is universally applicable or just a Unix-ism.

why not have main take a slice of os.Args?

Yeah, I'm not sure why it wasn't func main(args []string). Maybe they didn't want to overfit to Unix. I'm just guessing, though.

@networkimprov
Copy link

I suggest a read through the feedback wiki
https://github.com/golang/go/wiki/Go2ErrorHandlingFeedback

Most of it is counter-proposals; I suspect the next draft will be rather different :-)

In non-returning functions (main & goroutines) I suspect a "default handler" -- or explicit one that doesn't os.Exit() -- would panic(err) so that deferred ops run. If you want a log.Fatal(err), you have to code it.

@nigeltao
Copy link
Contributor Author

Oh, I'm sure that the next Error Handling draft will be rather different. I just wanted to write this idea down before I forgot.

@magical
Copy link
Contributor

magical commented Sep 13, 2018

Related: #24869, which proposed that main.main be allowed to return an integer exit code (but was quickly rejected).

@rsc
Copy link
Contributor

rsc commented Sep 19, 2018

This seems like a clear "no" to me. If anything, the new handle-check proposal is an argument against doing this, since if we adopt that proposal you'd be able to write:

func main() {
    handle err {
        log.Fatal(err)
    }
    ... check os.Open ...
    etc
}

I see very little compelling benefit here and I strongly disagree with establishing a standard handling of what happens when main returns an error. Your example of by default printing the error and exit 1 is one possibility but there are plenty of others. Better to let main be in charge of that.

More generally, we've already made the decision about what main is. I don't see a strong reason here to reopen it.

@rsc rsc closed this as completed Sep 19, 2018
@golang golang locked and limited conversation to collaborators Sep 19, 2019
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

7 participants