Go RPC Requirements

The net/rpc package stipulates that only methods that satisfy the following criteria will be made available for remote access; other methods will be ignored.

The method’s type is exported.

The method is exported

The method has two arguments, both exported (or builtin types).

The method’s second argument is a pointer

The method has return type error

Lets take one of our functions and turn it into a method that meets this criteria.

func MakeToDo(todo ToDo) ToDo { todoSlice = append(todoSlice, todo) return todo }

First, we need to make a method type and make sure it’s exported (make sure the name is capitalised)

type Tasks int

Then we use it as a method receiver.

func (t *Tasks) MakeToDo(todo ToDo) ToDo { }

The method is already exported (because the name, MakeToDo , is capitalised) so the next thing to do is change the methods arguments. From the RPC documentation:

The method’s first argument represents the arguments provided by the caller; the second argument represents the result parameters to be returned to the caller. The method’s return value, if non-nil, is passed back as a string that the client sees as if created by errors.New. If an error is returned, the reply parameter will not be sent back to the client.

func (t *Tasks) MakeToDo(todo ToDo, reply *ToDo) ToDo { }

Now the method has two arguments, todo and reply , and both have builtin or exported types. Notice that the reply type is ToDo , which is what we expect to return to the client if not an error .

The first argument ( todo ) will be provided by the caller; the second argument ( reply ) represents what will be returned to the caller.

We have also covered the next requirement, that the method’s second argument is a pointer.

That leaves one thing left to do; change the return type to be error .

func (t *Tasks) MakeToDo(todo ToDo, reply *ToDo) error { }

Our method now follows the RPC rules but, if you were to try and run it the same way as before, you will get errors. This is due to two things we need to refactor:

The method body. We are currently returning todo , but the method is expecting an error to be returned.

, but the method is expecting an to be returned. The method execution. Firstly we can no longer call it like a function ( MakeToDo(finishApp) ), because it’s a method, and secondly we need to amend the arguments, because it now expects two.

Let’s address the method body first. A quick reminder about the Go requirements for an RPC Method:

The method’s first argument represents the arguments provided by the caller; the second argument represents the result parameters to be returned to the caller. The method’s return value, if non-nil, is passed back as a string that the client sees as if created by errors.New. If an error is returned, the reply parameter will not be sent back to the client.

The important parts, that outline what we need to change in our method body, are:

The second argument represents the result parameters to be returned to the caller.

The methods return value, if non-nil, is passed back as a string that the client sees as if created by errors.New

If an error is returned, the reply parameter will not be sent back to the client.

This means that, instead of returning a value, we use a pointer to the reply parameter and return nil to indicate no errors. If we do want to return an error we just return it and the reply parameter will not be sent back to the client.

func (t *Task) MakeToDo(todo ToDo, reply *ToDo) error { todoSlice = append(todoSlice, todo) *reply = todo return nil }

Now lets fix how we’re calling the method, and the parameters we’re passing. First, in our main function, we need to register a new object that we can call our method on.

func main() { task := new(Task) }

We now have an object ( task ) that we can use to call our methods. We can now change our method invocation from this:

MakeToDo(finishApp)

To this:

task.MakeToDo(finishApp)

We now only have one more problem to fix, and that’s our parameters.

func main() { task := new(Task) var err error finishApp := ToDo{"Finish App", "Started"} var makeReply ToDo err = task.MakeToDo(finishApp, &makeReply) if err != nil { log.Fatal("Issue making ToDo: ", err) } }

We have now declared three new variables:

err that is of type error

that is of type finishApp that is of type ToDo

that is of type makeReply that is of type ToDo

We then call our MakeToDo method with the finishApp parameter like before, and an additional parameter &makeReply . The & denotes that this is a pointer to our makeReply variable. This means that the makeReply variable will be updated with the result of our method. Remember the Go RPC spec states the method’s first argument represents the arguments provided by the caller; the second argument represents the result parameters to be returned to the caller which is what we have defined.

Because the MakeToDo method has a return type of error , we can use err = MakeToDo(…) to determine whether an error was returned or not (in our case it’s nil ).

Now we need to change the rest of the functions to be RPC compliant methods.

A few key things to point out: