Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Go interface return type

I have an interface like this:

type ViewInterface interface{
    Init() View
}

type View struct{
    Width  int
    Height int
}

So I create a new type from View

type MainView View

func (m MainView) Init() MainView{
 return MainView{
   Width:10,
   Height:10,
 }
}

then I pass the MainView to the following method:

func Render(views ...ViewInterface){
  for _, view := range views {
     v := view.Init()
  }
}

func main() {
  Render(MainView{})
}

But I get this error:

cannot use MainView literal (type MainView) as type ViewInterface in argument to Render: MainView does not implement ViewInterface (wrong type for Init method)
have Init() MainView
want Init() View

Why MianView is not same as View? what is the right way to solve this problem?

thanks

like image 846
Saeed M. Avatar asked Dec 09 '18 03:12

Saeed M.


Video Answer


2 Answers

Go has different inheritance model comparing to mainstream languages like Java and C#.

Why MianView is not same as View?

Because they are defined differently.

Init function of MainView returns MainView while interface requires to return View.

Signature of Init method looks strange, it requires instance of structure because it is structure method and returns new instance of the same structure type.

Try to design interface around logic of your structures instead of their construction/lifetime:

type ViewInterface interface{
    Render()
}

type MainView View

func (m MainView) Render() {
  // do something
}

type AnotherView

func (m AnotherView) Render() {
  // do something else
}

func Render(views ...ViewInterface){
  for _, view := range views {
     view.Render()
  }
}
like image 128
Dmitry Harnitski Avatar answered Oct 06 '22 02:10

Dmitry Harnitski


Because type MainView View is a "defined type" and "is different from any other type, including the type it is created from.".

Instead you can use a type alias. type MainView = View.


But really the problem is the design of ViewInterface and Init().

Init() is written like a class method. Go doesn't have class methods (or, strictly speaking, classes). You create the struct and call methods on it. Simple initialization can be done right then.

view := View{ Width: 10, Height: 10 }

If you want to define a method to initialize the values consistently it would act on an existing struct and return nothing.

type ViewInterface interface{
    Init()
}

type View struct{
    Width  int
    Height int
}

func (v *View) Init() {
    v.Width = 10
    v.Height = 10
}

view := View{}
view.Init()

Then MainView can also define Init().

type MainView struct {
    X int
    Y int
}

type (mv *MainView) Init() {
    mv.X = 23
    mv.Y = 42
}

Because Init() takes a pointer, to satisfy ViewInterface you have to pass in pointers.

func main() {
    view := View{}
    mv := MainView{}
    Render(&view, &mv)
}

But what is Render() doing initializing objects anyway? That should already be done. It should be rendering. Interfaces should be all about common functionality with no regard to how its implemented. The things implementing ViewInterface should already be initialized.

Instead, you might say that a ViewInterface must have a Render method.

type ViewInterface interface{
    Render()
}

Then View and MainView can be structured however you like as long as they implement Render().

func (v View) Render() {
    fmt.Println("View!")
    fmt.Println(v)
}

func (mv MainView) Render() {
    fmt.Println("MainView!")
    fmt.Println(mv)
}

Then aRender() can take a list of things which implement ViewInterface and call Render() on each of them.

func Render(views ...ViewInterface){
  for _, view := range views {
     view.Render()
  }
}

Initialize them before passing them in. And now there's no need to pass pointers.

func main() {
    view := View{}
    view.Init()
    mv := MainView{}
    mv.Init()
    Render(view, mv)
}

Finally, Markus suggested in the comments using a package to get something like class methods.

# viewtest/main.go
package main

import(
    "log"
    "viewtest/view"
)

func main() {
    v := view.New()
    log.Printf("%#v", v)
}


# viewtest/view/view.go
package view

type View struct {
    Width  int
    Height int
}

func New() View {
    return View{Width: 10, Height: 10}
}

Go packages take a little getting used to, Go has firm ideas about how your project must be structured. I suggest this tutorial.

like image 30
Schwern Avatar answered Oct 06 '22 02:10

Schwern