The Go proposal committee has declined the try proposal.

I am disappointed. I also think this is open source working exactly as it should.

The proposal was not dropped because one person with commit access disliked it. A concrete design was published, tools were written to try it on existing code, hundreds of examples and objections were discussed, and the people behind it kept answering questions long after I would have quietly disconnected my router and moved to the woods.

Most importantly, the Proposal Review Committee made its decision in response to that discussion. The closing note mentions problems the original design had missed, including debugging prints and code coverage. It also admits something more fundamental: many Go programmers do not agree that the verbosity of error forwarding is a problem worth changing the language to solve.

That is a good reason to stop. A language belongs to the people who have to read it, and a change to control flow needs more than a clever specification. The community worked through the tradeoffs together and the committee made the call. I may disagree with the result, but I trust the process far more after seeing it work.

Still, I think this will eventually look like a missed opportunity.

The argument against try was strongest when it was used inside nested expressions:

return write(encode(try(read(name))))

I do not want to debug that either. But bad nesting was not the only possible future. The ordinary form was much less alarming:

b := try(ioutil.ReadFile(name))
try(json.Unmarshal(b, &cfg))

One operation per line, errors still in the function signature, no exceptions, and an explicit if whenever local handling mattered. I think that style would have become normal very quickly. It looks unusual today mostly because it is new.

There is a difficult bias in evaluating syntax: familiar boilerplate does not feel like syntax anymore. We see if err != nil and read “return the error.” We see try and inspect every unusual corner because it is new. That scrutiny is necessary before changing a language, but it is not a neutral comparison.

Rust went through a remarkably similar argument around try! and later ?. People worried about hidden control flow and two ways to write the same thing. Once programmers had lived with it, many came to consider it one of the language’s better features. Go is not Rust and should not become Rust, but people do learn new control-flow notation. We already did it with defer and go.

There is another group absent from a discussion among current Go programmers: people who did not choose Go because they found its error handling tedious. It is almost impossible to measure them from inside the community. The people most comfortable with the status quo are, unsurprisingly, the people who stayed.

None of that means the committee should have ignored the reaction and forced try through. That would have damaged the language and the community more than a little error boilerplate ever could. Declining it was the right decision now, given the lack of agreement about both the problem and the solution.

But I suspect we solved the social problem by leaving the technical one untouched. Years from now we may add something remarkably similar under another name, after enough other languages have made the idea feel ordinary. Or we may simply keep writing the same four lines and insist they are valuable because we have become very fast at not seeing them.

Either way, try was worth trying. The proposal improved our understanding of the problem, and its rejection proved that the Go community can say no even after a great deal of work has been invested.

That is not failure. I just hope it is not the last word on improving error handling.