dummy marker method in interface

1,720 views
Skip to first unread message

jonathan...@gmail.com

unread,
Jun 12, 2015, 11:12:15 PM6/12/15
to golan...@googlegroups.com
I find myself adhering to the following pattern:


type errKindOne interface {

    //real methods



    kindOne()

}



type errKindTwo interface {

    //real methods



    kindTwo()

}



type concreteErrOne struct {

    //data

}



func (c concreteErrOne) kindOne() {}



type concreteErrTwo struct {

    //data

}



func (c concreteErrTwo) kindOne() {}

func (c concreteErrTwo) kindTwo() {}


Is this idiomatic?
Basically I use type assertions in central handler wrappers that can check which kind of error it is. Feels strange though because those dummy methods are just there so the type assertions work. Any way that is better?

Chris Manghane

unread,
Jun 13, 2015, 1:14:49 AM6/13/15
to jonathan...@gmail.com, golan...@googlegroups.com

This doesn't look very idiomatic. What is the problem you're solving? Some example code demonstrating use of this pattern might be helpful.


--
You received this message because you are subscribed to the Google Groups "golang-nuts" group.
To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Paul Hankin

unread,
Jun 13, 2015, 1:18:59 AM6/13/15
to golan...@googlegroups.com, jonathan...@gmail.com
Assuming you have multiple error types of kind one and kind two (otherwise a simple type switch suffices), It seems easier to not use interfaces and simply write a function:

func IsKindOne(err error) bool {
  switch err.(type) {
  case concreteErrOne, concreteErrAnotherOne:
      return true
  default:
      return false
  }
}

It's hard to say in general though without more context.

-- 
Paul Hankin

jonathan...@gmail.com

unread,
Jun 13, 2015, 6:41:01 PM6/13/15
to golan...@googlegroups.com, jonathan...@gmail.com
Paul I thought about that, but then when you go to do the type switch where you need to check the error you can't get a field without multiple cases:

switch err := someErr.(type) {
case concreteErrOne, concreteErrAnother:
  //a field that is in only one type can't be accessed here so we need another switch
  
  var field1 string
  var field2 string  
  switch err2 := err.(type) {
    concreteErrOne:
      field1 = err2.field1
    concreteErrAnother:
      field2 = err2.field2
  }

  //use the two fields to handle error
}

which seems worse than the dummy methods. I actually see the dummy method route in the net package source, as well as the Is..() func route as well as straight type switching :)
Just wondering if I was missing an easy pattern but this will still work for now..

Chris Manghane

unread,
Jun 13, 2015, 7:24:04 PM6/13/15
to jonathan...@gmail.com, golan...@googlegroups.com

I'm confident you are missing an easy pattern,  but without some real code, there's not much to say. Any links to your projects that use this? Your pattern seems pretty odd, mostly because I assume these two error interfaces have a distinct method set which would make the marker functions redundant. If they don't, why not just make concrete types that implement the Error interface and skip the weird type assertions?


--

jonathan...@gmail.com

unread,
Jun 14, 2015, 12:19:42 AM6/14/15
to golan...@googlegroups.com, jonathan...@gmail.com
Ok take these definitions:

type httpError interface {
    key() string
    code() int
    msg() string
}

type internalError interface {
    internal() bool
}

type hijackError interface {
    hijack() bool
}

//satisfies httpError and error
type basicError struct {
    k string
    c int
}

func (b basicError) Error() string {
    return fmt.Sprintf("%v %v", b.k, b.c)
}
func (b basicError) key() string {
    return b.k
}
func (b basicError) code() int {
    return b.c
}
func (b basicError) msg() string {
    return http.StatusText(b.c)
}

//satisfies httpError and error
type msgError struct {
    basicError
    m string
}

func (m msgError) Error() string {
    return fmt.Sprintf("%v %v", m.basicError.Error(), m.m)
}
func (m msgError) msg() string {
    return m.m
}

//satisfies httpError, internalError and error
type intError struct {
    basicError
    e error
}

func (i intError) Error() string {
    return fmt.Sprintf("%v %v", i.basicError.Error(), i.e)
}
func (i intError) internal() bool {
    return true
}

//satisfies hijackError, internalError and error
type hjError struct {
    e error
}

func (h hjError) Error() string {
    return h.e.Error()
}
func (h hjError) hijack() bool {
    return true
}
func (h hjError) internal() bool {
    return true
}


In a wrapper for http handlers, these are used in different ways. If it is an internalError, it is logged. If it is a httpError the info is given back to the user. If it is a hijackError, the connection is hijacked and forcibly closed, as a last ditch effort to give the user an error (in the case where we already wrote output). But you can see there is more than one type that implement internalError and more than one type for httpError. A way around this I suppose would be to have one type of error struct that has all the needed fields and just create them with nils when they are not used. Which would be okish except you can't really reuse a basicError for instance to make a msgError, unless you have a func that take the basicError and copies fields to the msgError.

I'm probably missing something.

Nick Craig-Wood

unread,
Jun 15, 2015, 6:29:02 AM6/15/15
to jonathan...@gmail.com, golan...@googlegroups.com
On 13/06/15 04:11, jonathan...@gmail.com wrote:
> I find myself adhering to the following pattern:
>
> type errKindOne interface {
> //real methods
> kindOne()
> }

The go ast parser uses exactly this pattern, check
http://golang.org/src/go/ast/ast.go and search for exprNode for example.

I've used it too when making my own AST.

When you have a lot of objects which you need to classify in some way it
works quite well. You can use it to mimic an inheritance hierarchy if
you want.

--
Nick Craig-Wood <ni...@craig-wood.com> -- http://www.craig-wood.com/nick
Reply all
Reply to author
Forward
0 new messages