errors.As(e,&xe) or returning XError from function ?

155 views
Skip to first unread message

andreas graeper

unread,
Aug 18, 2025, 12:28:44 PMAug 18
to golan...@googlegroups.com
type XError struct { i int }

func f() (int,error) {
 return 0,XError{110} // bad-case
 return 110,nil    // good-case
}

if r,e := f() ; e!=nil {
 println(e.i) // does not work
 var xe XError
 if errors.As(e,&xe) { println(xe.i) }
}

fun f() (int,XError) {
  return 0,XError{110}    // seems to work
  return 123,nil // nil for XError is not possible
}
if r,e = f() ; e!=nil { println( e.i ) } // ok

fun f() (int,*XError) {
  return 123,nil // this works
}

type-interface (error) can be nil
type-struct (XError) cannot be nil, i need to replace with *XError ?

is interface s.t like reference-type and struct a value-type ?

thanks++, Andi






Jason E. Aten

unread,
Aug 18, 2025, 8:52:54 PMAug 18
to golang-nuts
The new go.dev website makes the language Spec hard to find, which is unfortunate.

The spec is here -- it is one of the easiest language specs to read. 


The section on the built-in (pre-declared) error interface is here

Although struct values are allowed, they are very difficult to use correctly. I even
mess them up sometimes.

So until you get your feet with Go, I strongly recommend that you stick to only defining 
methods on pointers to structs, and passing around pointers to structs. Otherwise
you will likely shoot yourself in the foot by trying to update a value that cannot be
updated, and will wonder why your updates were lost.

To create a struct that implements the error
interface, you need to define all the methods in the error interface. Fortunately, there
is only one.

type error interface { 
 Error() string 
}

So your XError method could look look like this:

func (x *XError) Error() string {
   return fmt.Sprintf("XError: i = %v", x.i)
}

Then have methods return &XError{i: 99} or whatever you need.

func getAnError() error {
   return &XError{i: 99}

Jan Mercl

unread,
Aug 19, 2025, 5:34:12 AMAug 19
to Jason E. Aten, golang-nuts
On Tue, Aug 19, 2025 at 2:53 AM Jason E. Aten <j.e....@gmail.com> wrote:

> The new go.dev website makes the language Spec hard to find, which is unfortunate.

It was hard to navigate to but now it is actually the first item in
the "Docs" section. That's much better now, IMO:
https://opu.peklo.biz/p/25/08/19/1755595842-b5d77.png

Brian Candler

unread,
Aug 25, 2025, 11:01:31 AM (11 days ago) Aug 25
to golang-nuts
On Monday, 18 August 2025 at 17:28:44 UTC+1 andreas graeper wrote:
type-interface (error) can be nil
type-struct (XError) cannot be nil, i need to replace with *XError ?

is interface s.t like reference-type and struct a value-type ? 

interfaces are like boxes, containing a <concrete-type, ref-to-value> pair.  A nil interface is effectively <nil, nil>  - no type and therefore no value.

A nil interface is not the same as a nil pointer.  Hence, an interface containing any concrete value of type *foo (even if that value is nil) is not a nil interface value.

And if your function returned a *XError type, even if it returned a nil pointer, but the caller assigned it to a variable which holds an "error" type (which is an interface), this would show as a not-nil interface value.

The normal rule in Go is "return concrete types, accept interfaces". This gives maximum flexibility and type safety. However, errors are a special case: conventionally you return a value of interface type "error". In that case, a nil (interface) value means "no error" and any other value means "some error". Since it's an interface, you know that any non-nil value implements the Error() method.

Therefore, you did the right thing here:

func f() (int,error) {
 return 0,XError{110} // bad-case
 return 110,nil    // good-case
}

although a separate convention is that it's recommended that error values are pointers - this was discussed recently on this group.
Reply all
Reply to author
Forward
0 new messages