Deep Introduction to Go Interfaces.

Source: Internet
Author: User
Tags aliases
This is a creation in Article, where the information may have evolved or changed.

Standard Interface Intro

Go ' s interfaces is one of it's best features, but they ' re also one of the most confusing for newbies. This post would try to give your understanding you need to use Go ' s interfaces and not get frustrated when things don ' t Work the expect. It's a little long, but a bunch of which is just code examples.

Go ' s interfaces is different than interfaces in other languages, they is implicitly fulfilled. This means, never need to mark your type as explicitly implementing, the interface (like class Cfoo implements IFoo ). Instead, your type just needs to has the methods defined in the interface, and the compiler does the rest.

For example:

type Walker interface {    Walk(miles int)}type Camel struct {    Name string}func (c Camel) Walk(miles int) {     fmt.Printf(“%s is walking %v miles\n”, c.Name, miles)}func LongWalk(w Walker) {     w.Walk(500)     w.Walk(500)}func main() {    c := Camel{“Bill”}    LongWalk(c)}// prints// Bill is walking 500 miles.// Bill is walking 500 miles.

Http://play.golang.org/p/erodX-JplO

Camel implements the Walker interface, because it had a method named Walk that takes an int and doesn ' t return anything. This means can pass it into the Longwalk function, even though you never specified that your Camel is a Walker. In fact, Camel and Walker can live in totally different packages and never know about one another, and this would still wor K If a third package decides-a Camel and pass it into Longwalk.

Non-standard continuation

This is the where most tutorials stop, and where the most questions and problems begin. The problem is so still don ' t know how the interfaces actually work, and since it's not actually that complicated, l Et's talk on that.

What actually happens when do you pass the Camel into Longwalk?

So, first off, your ' re not passing Camel into Longwalk. You ' re actually assigning C, a value of type Camel to a value W of type Walker, and W are what you operate on Longwalk.

Under the covers, the Walker interface (like all interfaces), would look more or less like this if it were in Go (the ACTU Al code is in C, so the is just a really rough approximation, which is easier to read).

type Walker struct {    type InterfaceType    data *void}type InterfaceType struct {    valtype *gotype    func0 *func    func1 *func    ...}

All interfaces values is just, pointers-one pointer to information on the interface type, and one pointer to the Data from the value of passed into the interface (a void in c-like languages ... this should probably is Go ' unsafe. Pointer, but I liked the explicitness of both actual * ' s in the struct to show it's just II pointers).

The interfacetype contains a pointer to information, about the type of the value, you passed into the interface (Valtyp e). It also contains pointers to the methods that is available on the interface.

When you assign C to W, the compiler generates instructions that looks more or less like this (it's not actually generatin G Go, this was just an easier-to-read approximation):

data := cw := Walker{     type: &InterfaceType{               valtype: &typeof(c),               func0: &Camel.Walk           }    data: &data}

When you assign your Camel value C to the Walker value W, the camel type was copied into the interface value ' s Type.valtype Field. The actual data in the value of C is copied to a new place in memory, and W's data field points at that memory location.

Implications of the implementation

Now, let's look at the implications of this code. First, interface values are very small-just and pointers. When you assign a value of a interface, that's value gets copied once, into the interface, but after that, it's held in A pointer, so it doesn ' t get copied again if you pass the interface around.

So now you know why do you don ' t need to pass around pointers to Interfaces-they ' re small anyway, so you don't have to Worr Y about copying the memory, plus they hold your data in a pointer, so changes to the data would travel with the interface.

Interfaces is Types

Let's look at Walker again, this is important:

Type Walker interface

Note that first word there:type. Interfaces is types, just as a string is a type or Camel is a type. They aren ' t aliases, they ' re not magic hand-waving, they ' re real types and real values which is distinct from the type an D value that gets assigned to them.

Now, let's assume you has this function:

Func Longwalkall (Walkers []walker) {for _, W: = Range Walkers {longwalk (W)}}

And let's say you had a caravan of camels that's want to send in a long walk:

caravan := []Camel{ Camel{“Bill”}, Camel{“Bob”}, Camel{“Steve”}}

Want to pass caravan into Longwalkall, would the compiler let you? Nope. Why was that? Well, []walker is a specific type, it's a slice of values of type Walker. It's not shorthand for "a slice of anything that matches the Walker interface". It's an actual distinct type, the "the" []string is different from []int. The Go compiler would output code to assign a single value of Camel to a single value of Walker. That's the only place it ' ll help you out. So, with slices, you had to do it yourself:

walkers := make([]Walker, len(caravan))for n, c := range caravan {    walkers[n] = c}LongWalkAll(walkers)

However, there's a better if you know you'll just need the caravan for passing into Longwalkall:

caravan := []Walker{ Camel{“Bill”}, Camel{“Bob”}, Camel{“Steve”}}LongWalkAll(caravan)

Note that this goes for any type which includes a interface as part of the its Definition:there ' s no automatic conversion of Your func (Camel) into the func (Walker) or Map[string]camel into Map[string]walker. Again, they ' re totally different types, they ' re not shorthand, and they ' re not aliases, and they ' re not just a pattern for The compiler to match.

Interfaces and the pointers that Satisfy them

What if Camel's Walk method had this signature instead?

func (c *Camel) Walk(miles int)

This line says, the type *camel has a function called Walk. This is important: *camel is a type. It ' s The ' pointer to a Camel ' type. It's a distinct type from (Non-pointer) Camel. The part on it being a pointer is part of the IT type. The Walk method is on the type *camel. The Walk method (in this new incarnation) isn't on the type Camel. This becomes important if you try to assign it to an interface.

c := Camel{“Bill”}LongWalk(c)// compiler output:cannot use c (type Camel) as type Walker in function argument: Camel does not implement Walker (Walk method has pointer receiver)

To pass a camel into longwalk now, you need to pass in a pointer to a camel:

c := &Camel{“Bill”}LongWalk(c)orc := Camel{“Bill”}LongWalk(&c)

Note that this is true even though you can still call Walk directly on Camel:

c := Camel{“Bill”}c.Walk(500) // this works

The reason you can do, is, the Go compiler automatically converts this line to (&C). Walk ($) for you. However, that's doesn ' t work for passing the value to an interface. The reason is the value of a interface is in a hidden memory location, and so the compiler can ' t automatically get a Pointer to the memory for your (in Go parlance, this is known as being "not addressable").

Nil pointers and Nil Interfaces

The interaction between nil interfaces and nil pointers is where nearly everyone gets tripped up when they first start wit H Go.

Let's say we have our Camel type with the Walk method defined on *camel as above, and we want to make a function that Retu RNs a Walker that's actually a Camel (note that you don't need a function to does this, you can just assign a *camel to a W Alker, but the function is a good illustrative example):

func MakeWalker() Walker {    return &Camel{“Bill”}}w := MakeWalker()if w != nil {    w.Walk(500)  // we will hit this}

This works fine. But now, what if we do something a little different:

func MakeWalker(c *Camel) Walker {    return c}var c *Camelw := MakeWalker(c)if w != nil {    // we’ll get in here, but why?    w.Walk(500)}

This code would also get inside the IF statement (and then panic, which we'll talk about in a bit) because the returned Wal Ker value is not nil. How are that possible, if we returned a nil pointer? Well, let's go look back to the instructions that get generated if we assign a value to an interface.

data := cw := Walker{     type: &InterfaceType{               valtype: &typeof(c),               func0: &Camel.Walk           }    data: &data}

In the case, the C is a nil pointer. However, that's a perfectly valid value to assign to the Walker's Data value, so it works just fine. What are you return are a Non-nil Walker value, that have a pointer to a nil *camel as its data. So, for course, if you check w = = Nil, the answer is false, W was not nil ... but then inside the IF statement, we try to call Camel ' s walk:

func (c *Camel) Walk(miles int) {     fmt.Printf(“%s is walking %v miles\n”, c.Name, miles)}

And when we try to do c.name, Go automatically turns (*c). Name, and the code panics with a nil pointer dereference error.

Hopefully this makes sense, given we new understanding of how interfaces wrap values Pointers? Assume want Makewalker to return a nil interface if it gets passed a nil Camel. Explicitly assign nil to the interface:

func MakeWalker(c *Camel) Walker {    if c == nil {        return nil    }    return c}var c *Camelw := MakeWalker(c)if w != nil {    // Yay, we don’t get here!    w.Walk(500)}

And now, finally, the code was doing what we expect. When your pass in a nil *camel, we return a nil interface. Here's an alternate-to-write the function:

func MakeWalker(c *Camel) Walker {    var w Walker    if c != nil {        w = c    }    return w}

This was slightly less optimal, but it shows the other by-the-get-a nil interface, which is-use the-zero value for the I Nterface, which is nil.

Note that can has a nil pointer value of satisfies an interface. You just need to being careful not to dereference the pointer in your methods. For example, if *camel ' s Walk method looked like this:

func (c *Camel) Walk(miles int) {    fmt.Printf(“I’m walking %d miles!”, miles)}

Note that this method does not dereference C, and therefore you can call it even if C is nil:

var c *Camelc.Walk(500)// prints “I’m walking 500 miles!”


source POST: http://npf.io/2014/05/intro-to-go-interfaces/
Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.