4

I have a tool that I'm writing that exposes some functions that pull information out of a static database to several scripting languages that I'm embedding into the tool.

I thought; "Hey sounds like a nice use case for interfaces". So I defined an interface like so in my package scripting

type ScriptingLang interface {
    RunScript(filename string) error
    RunString(s string) error
    Interpreter() error
    Init() error
    IsInit() bool
}

Then I store a map of them so I can look them up by a string defined like so in a different package.

var ScriptingLangs = make(map[string]scripting.ScriptingLang)

and a function to register them. Also some little helper functions like

func RunString(lang, s string) error {
    if v, ok := ScriptingLangs[lang]; ok {
        if !v.IsInit() {
            v.Init()
        }
        return v.RunString(s)
    } else {
        return NoSuchLangErr
    }
    return nil
 }

The problem that I ran into is it seams that interfaces can't have methods with pointer receivers. As a result my Lua struct that implements ScriptingLang isn't able to save it's *state because it's stored in ScriptingLangs.

I've tried updating the value stored in the map at the end of functions that save state and it didn't update the value.

To my understanding you shouldn't use pointers of interfaces so what are my options here? I would like to really keep the interfaces so I can do some neat stuff with git submodules.

A minimal example of my problem:

package main

import (
    "fmt"
)

type ScriptingLang interface {
    DoString(s string) error
    Init() error
}

type Lua struct {
    state string
}

func (l Lua) DoString(s string) error {
    fmt.Printf("Doing '%v' with state '%v'\n", s, l.state)
    return nil
}

func (l Lua) Init() error {
    l.state = "Inited"
    return nil
}

var lang ScriptingLang

func main() {
    lang = Lua{}
    lang.Init()
    lang.DoString("Stuff")
}
4
  • Are you saying that a pointer type can't implement an interface? It's sure that they can. Can you create a minimal example that demonstrates the problem you're having? At the moment, there's lots of context in your question, but none of the important details. Commented May 5, 2015 at 3:59
  • No. I'm under the impression that a pointer of an interface is unable to use call methods. Also a struct is unable to update any of it's fields, because interfaces are not allowed pointer methods. I did update with a minimal example of what's going on though. Thanks for help in advance! Commented May 5, 2015 at 4:15
  • 2
    BTW, a few side notes: When naming try to avoid stutter (e.g. scripting.Lang vs scripting.ScriptingLag). Error variables are often named Err… and error types …Error (e.g. var ErrNoSuchLang = errors.New(…) or type SomeCategoryError …). Watch out for data races if you intend to use any of this via goroutines; as-is your Init example is not safe for concurrency (which is fine if you expect the caller to deal with that); sync.Once can be helpful in such cases as well as the race detector. Commented May 5, 2015 at 16:53
  • Apart from the races, this has all been fixed. Godoc.org's linters bugged me about most of it. The tool doesn't use any Goroutines for the moment. Thanks Dave! Commented May 5, 2015 at 18:08

1 Answer 1

7

If you want to mutate state, you need a pointer receiver, and your Init method doesn't have one. The fact that you're storing the value inside an interface makes no difference.

In your minimal(-ish) example, change the Init method (and any method that updates state) to have a pointer receiver, and point a pointer inside the interface and everything works:

func (l *Lua) Init() error {
    l.state = "Inited"
    return nil
}

...

func main() {
    lang = &Lua{}
    lang.Init()
    lang.DoString("Stuff")
}

This article might help: http://jordanorelli.com/post/32665860244/how-to-use-interfaces-in-go

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

3 Comments

That won't satisfy the interface though; Lua does not implement ScriptingLang (Init method has pointer receiver)
I'm facepalming so hard right now. For whatever reason I was attempting to store the pointer inside the interfaces as *ScriptingLang. Much thanks on helping me understand that.
Really great explanation and great example. Saved me a lot of time. Thanks.

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.