-
Notifications
You must be signed in to change notification settings - Fork 18.4k
Description
The error
system is great, but sometimes it's hard to figure out where an error came from. Here's a proposal that could more or less be implemented today with a Golang source code transpiler, but would benefit from official Golang support. This is a proposal for Go1. (For Go2 I would rather propose that error
be a tracer itself)
The proposal is to support the following:
- a global interface like type
terror interface {}
(name can change) - in
"pkg/errors"
,func Terror(e error) terror {...}
as implemented below (name can change) - in
"pkg/errors"
, keep the same signature inerrors.New() error {...}
but have it return aterror
that can be run-time converted.
// New global type.
// Like `error`, a global interface.
// Trace calls `runtime.Caller` to get the file and line number.
type tracer interface {
Trace()
}
// New global type.
// A new type of error that traces.
type terror interface {
error
tracer
}
// In pkg/errors.
func Terror(e error) terror {
if t, ok := e.(terror); ok {
return t
}
return newTerror(e)
}
// In pkg/errors.
// Same method signature, but actually returns a terror.
func New(...) error {..}
// Usage:
var e error = errors.New("bark")
var t1 terror := e // compile error
var t2 terror := e.(terror) // ok
var t3 terror := errors.Terror(e) // ok
Everything else can be handled by third-party libraries and code transpilers. What follows is just one example of syntax support from a transpiler with support for an assignment modifier !
.
func foo() terror {
var x terror = errors.New("bark").(terror)
var x! terror = x // trace!
var y error = x
var z! error = x // compile error
return x
}
x := foo()
x! = x // trace!
x! = <-ch // trace!
var y, z *terror
y = z
y! = z // trace! (but is this what we want?)
*y = x
*y! = x // trace!
type MyStruct struct {
t terror
}
s := MyStruct{x}
s = MyStruct{field!:x} // trace!
s2 := s
s2! := s // compile error s.(tracer) is not ok
var i interface{}
i = x
i2 := i.(terror)
i3! := i.(terror) // trace!
func bar(t! terror) {...}
bar(x) // trace @ bar()!
The pain-point with error
is that it doesn't trace. This seems like a way to inject tracing with minimal syntactic change and good performance.
The transpiler could also work without explicit !
, but rather automatically every time something is assigned to a var _ terror
.