Question

I am implementing an application layer network protocol which uses JSON in Go.

func ReadMessage(conn net.Conn, returnMessage interface{}) bool {
    messageBytes := // read from conn

    error := json.Unmarshal(messageBytes, &returnMessage)
    if error != nil {
        return false
    }

    return true
}

The function takes a struct as its second parameter where the message is unmarshalled. The function can be called like this:

msg := MessageType1{}
ok := ReadMessage(conn, &msg)

Or without the ampersand (&)

msg := MessageType1{}
ok := ReadMessage(conn, msg)

which will compile, but not do what is should as the struct is passed as a copy, not as a reference and the original msg will remain empty. So I'd like to force passing the struct by reference and catch this error at compile time.

Changing the parameter type to *interface{} will not compile:

cannot use &msg (type *MessageType1) as type *interface {} in function argument:
*interface {} is pointer to interface, not interface

Is there some Go style way of doing this correctly?

Was it helpful?

Solution

There is not a way to do this in the function declaration.

You can use reflection though and panic at runtime when the argument is not a pointer.

However maybe you should consider changing the design of your code. The concrete type of the argument should not matter. It either implements the interface you need or not.

Demo: http://play.golang.org/p/7Dw0EkFzbx

OTHER TIPS

Since Go 1.18 you can do this using generics:

func test[T any](dst *T) {
  //Do something with dst
}

You can't enforce this as *T always has the method set of T. Thus both implement the interface. From the spec:

The method set of any other type T consists of all methods with receiver type T. The method set of the corresponding pointer type *T is the set of all methods with receiver *T or T (that is, it also contains the method set of T).

What you can do instead is to use the language's ability to return multiple values in your function, as Volker already stated:

func ReadMessage(conn net.Conn) (interface{}, bool) {
    var returnMessage interface{}
    messageBytes := // read from conn

    error := json.Unmarshal(messageBytes, &returnMessage)
    if error != nil {
        return nil, false
    }

    return returnMessage, true
}

You should also consider not returning type interface{} but some meaningful type.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top