Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Interfaces stored as value; Methods unable to update struct fields

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")
}
like image 511
THUNDERGROOVE Avatar asked Mar 16 '23 04:03

THUNDERGROOVE


1 Answers

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

like image 95
Paul Hankin Avatar answered Apr 06 '23 20:04

Paul Hankin