72

I've read "Effective Go" and other Q&As like this: golang interface compliance compile type check , but nonetheless I can't understand properly how to use this technique.

Please, see example:

type Somether interface {
    Method() bool
}

type MyType string

func (mt MyType) Method2() bool {
    return true
}

func main() {
    val := MyType("hello")

    //here I want to get bool if my value implements Somether
    _, ok := val.(Somether)
    //but val must be interface, hm..what if I want explicit type?

    //yes, here is another method:
    var _ Iface = (*MyType)(nil)
    //but it throws compile error
    //it would be great if someone explain the notation above, looks weird
}

Is there any simple ways (eg without using reflection) check value if it implements an interface?

1
  • 3
    how about _, ok := interface{}(val).(Somether) ? Commented Sep 23, 2016 at 6:24

8 Answers 8

102

You only have to check if a value implements an interface if you don't know the value's type. If the type is known, that check is automatically done by the compiler.

If you really want to check anyways, you can do it with the second method you gave:

var _ Somether = (*MyType)(nil)

which would error at compile time:

prog.go:23: cannot use (*MyType)(nil) (type *MyType) as type Somether in assignment:
    *MyType does not implement Somether (missing Method method)
 [process exited with non-zero status]

What you are doing here, is assigning a pointer of MyType type (and nil value) to a variable of type Somether, but since the variable name is _ it is disregarded.

If MyType implemented Somether, it would compile and do nothing

Sign up to request clarification or add additional context in comments.

4 Comments

Any reason why the black identifier does not necessarily need to be *Somether, since the right hand has a pointer to MyType? I'm still learning. :-)
you can think of an interface value like a container, you can put whatever you want in it as long as it implements the right methods. It can contain a pointer to a struct, or a struct directly. As a rule of thumb you never need to make a pointer to an interface value
This simple explanation is finally starting to click with me. What makes it weird is that on the left side of the equation the type is last and its an interface, so no need for a "*", then on the right side the type is first and its a pointer to a type.
I now use this to check when implementing interfaces to check that I did not miss any. My IDE will show me red squiggly lines, so I don't even need to build myself.
28

Following will work:

val:=MyType("hello")
var i interface{}=val
v, ok:=i.(Somether)

Comments

20

It is also possible to use Implements(u Type) bool method of reflect.Type in the following way:

package main

import (
    "reflect"
)

type Somether interface {
    Method() bool
}

type MyType string

func (mt MyType) Method() bool {
    return true
}

func main() {

    inter := reflect.TypeOf((*Somether)(nil)).Elem()

    if reflect.TypeOf(MyType("")).Implements(inter) {
        print("implements")
    } else {
        print("doesn't")
    }
}

You can read more on that in the documentation.

1 Comment

This is a canonical example that is nice documented along documentation. However, @Alpha solution is much prettier
14

You can also take Alpha's solution:

val := MyType("hello")
var i interface{} = val
v, ok := i.(Somether)

... and reduce it further:

val := MyType("hello")
v, ok := interface{}(val).(Somether)

If you're trying to test for one-off methods, you can even do something like:

val := MyType("hello")
v, ok := interface{}(val).(interface {
    Method() bool
}) 

NOTE: Make sure you are very careful with "pointer receiver" versus "value receiver" implementations. If the implementation uses a pointer the assertion will fail when passing in a value object. If the implementation uses a value receiver, both assertions will pass.

// Implement Somether as a POINTER receiver method
func (mt *MyType) Method() bool {
  return true
}

func main() {
    val := MyType("hello")

    v, ok := interface{}(val).(Somether)
    fmt.Println(v, ok)
    // Output:  <nil> false

    // Notice the pass by reference
    v, ok := interface{}(&val).(Somether)
    fmt.Println(v, ok)
    // Output:  0xc000010200 true

}

versus

// Implement Somether as a VALUE receiver method
func (mt MyType) Method() bool {
  return true
}

func main() {
    val := MyType("hello")

    v, ok := interface{}(val).(Somether)
    fmt.Println(v, ok)
    // Output:  hello true

    // Notice the pass by reference
    v, ok := interface{}(&val).(Somether)
    fmt.Println(v, ok)
    // Output:  0xc00008e1e0 true

}

Comments

3

I have a solution that I use to complement a panic handler pattern
I have not exhaustively tested the code but casual testing is affirmative
I welcome any suggestions to improve or other go expert stuff

// -------------------------------------------------------------- //
// hasErrIface -
// ---------------------------------------------------------------//
func hasErrIface(v reflect.Value) (error, bool) {
    // CanInterface reports whether Interface can be used without panicking
    if !v.CanInterface() {
        return nil, false
    }
    // Interface panics if the Value was obtained by accessing unexported struct fields
    err, ok := v.Interface().(error)
    return err, ok
}

// -------------------------------------------------------------- //
// HasErrKind
// ---------------------------------------------------------------//
func HasErrKind(r interface{}) (err error, isErr bool) {
    err = nil
    isErr = false
    v := reflect.ValueOf(r)
    switch v.Kind() {
    case reflect.Struct:
        errtype := reflect.TypeOf((*error)(nil)).Elem()
        if v.Type().Implements(errtype) {
            err, isErr = v.Interface().(error)
        }
    case reflect.Ptr:
        err, isErr = hasErrIface(v)
    case reflect.Interface:
        err, isErr = hasErrIface(v)
    }
    return
}

// -------------------------------------------------------------- //
// EvalErrKind
// ---------------------------------------------------------------//
func EvalErrKind(r interface{}) (errval error) {
    err, isErr := HasErrKind(r)
    if !isErr {
        errtxt := "Unknown system error - %v :\n%v"
        v := reflect.ValueOf(r)
        return fmt.Errorf(errtxt, v.Type(), v)
    }
    return err
}

// -------------------------------------------------------------- //
// PanicHandler
// ---------------------------------------------------------------//
func PanicHandler(errHandler func(error)) func() {
    return func() {
        var err error
        if r := recover(); r != nil {
            switch r.(type) {
            case ApiError:
                err = r.(ApiError)
            default:
                err = EvalErrKind(r)
            }
            if errHandler != nil {
                errHandler(err)
            }
        }
    }
}

Comments

2

It only works with Go 1.18 or higher

//implements interface
func implInter[T any](obj any) bool {
⠀ _, ok := obj.(T)
  return ok
}

var obj any = "some text"

if impleInter[string](obj) {
⠀ newObj, _ := obj.(string)
} 

Comments

0

You can also use this:

If MyType implements the Somether interface, this should compile

// use new to create a pointer to type MyType
var _ Somether = new(MyType) 


var _ Somether = MyType("")

Comments

0

Here is a simple way to check if a struct implements an interface. It is already pointed out by some others, but I want to show a clear, pragmatic way. Key here is do assertion on interface with a checking type.

type Somether interface { // Interface
    Method() bool
}

type MyType string // Struct or Type

func (mt MyType) Method2() bool {
    return true
}

func main() {
    var s Somether
    _, ok := s.(MyType) // Check if struct MyType satisfies Somether interface
    
}

This code will return impossible type assertion error stating, "MyType does not implement Somether (missing method Method)".

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.