如何在类型为map [int]的接口上实现接口

I'm trying to build a function, that can be implemented on a map[int]T. Im pretty new to go and wondered, if this can be done by implementing an interface.

    invoices   map[int]domain.Invoice
    bookings   map[int]domain.Booking
    projects   map[int]domain.Project

All of those have following in common:

type Invoice struct {
    ID         int
}
type Booking struct {
    ID         int
}
type Project struct {
    ID         int
}

How must I proceed to be able to implement a function that returns the next ID of all Invoices, Bookings or Projects by increasing the ID of the last Item in the respecting map of the Type?

For example:

func (i *map[int]T) nextID() int {
    return T.ID + 1

wondered, if this can be done by implementing an interface of course you can.

I dont pretend to provide the right idiomatic way. But from what you say, all those types can be summarized into an IDer interface.

For that pruposes they would have to implement an interface defined as type IDer interface { ID() int }. This ID() method would have to be implemented for each struct types. Using that common compatible implementation you can define a map[int]IDer and put into it Invoice, Booking, Project etc as long as it is compatible.

In order to prevent code duplication you could define a type IDed struct { ID int}; func (i ID) int {return i.ID}, and embed that within your types such as type Invoice struct { IDed } and so on. Doing so you can still call var x Invoice; x.ID=1; someid := x.ID;

Finally, you might define you map as a type type mapOfID map[int]IDer and attach methods to it func (m mapOfID) DoSomethigWithIDers(){...}

@mh-cbon outlined a couples of approaches. Maps by themselves do a poor job of keeping track of the "last" item since it's not part of their storage or semantics. One way around this would be to either keep track of the last index or the last domain object added to a map in order to calculate the next id:

type IDer interface {
    ID() int
}
type IDS struct {
    m    map[int]IDer
    last IDer
}

func (i *IDS) Add(index int, ider IDer) {
    i.m[index] = ider
    i.last = ider
}

func (i *IDS) nextID() int {
    if i.last == nil {
      return 1
    }
    return i.last.ID() + 1
}

The example above combines @mh-cbon's IDer interface with the ability to keep traack of the last Ider added.

This can then be used with any of your domain objects as long as they implement the IDer interface:

type Invoice struct{}

func (i Invoice) ID() int { return 1 }

func main() {
    ids := &IDS{
       m: make(map[int]IDer),
    }
    ids.Add(1, Invoice{})
    fmt.Printf("Next ID: %d
", ids.nextID())

}
Next ID: 2

Program exited.

With all the information gathered I came to following answer:

https://play.golang.org/p/3RAvclRacbh

(I used "List" for easier naming)


package main

import (
    "fmt"
)

type Invoice struct{
    Name string
    Id int
}

// Interface to implement
type IDer interface {
    ID() int
}
// Interface implementation for Invoice
func (i Invoice) ID() int { return i.Id }

type List struct {
    Items    map[int]IDer
    last IDer
}

func (i *List) Add(index int, ider IDer) {
    i.Items[index] = ider
    i.last = ider
}

func (i *List) nextID() int {
        if i.last == nil {
          return 1
        }
    return i.last.ID() + 1
}

type Repository struct {
    invoices List
}

func main() {
    r := Repository{}
    r.invoices = List{ 
        Items: make(map[int]IDer), 
    }

    i := Invoice{}
    i.Name = "Test"
    i.Id = 1
    r.invoices.Add(1, i)
    ia := r.invoices.Items[1].(Invoice) 
    fmt.Println(ia.Name)
    fmt.Printf("Next ID: %d
", r.invoices.nextID())


    i2 := Invoice{}
    i2.Name = "Test2"
    i2.Id = r.invoices.nextID()
    r.invoices.Add(i2.Id, i2)
    ia2 := r.invoices.Items[i2.Id].(Invoice)    
    fmt.Println(ia2.Name)
    fmt.Printf("Next ID: %d
", r.invoices.nextID())


    i3 := Invoice{}
    i3.Name = "Test3"
    i3.Id = r.invoices.nextID()
    r.invoices.Add(i3.Id, i3)
    ia3 := r.invoices.Items[i3.Id].(Invoice)    
    fmt.Println(ia3.Name)
    fmt.Printf("Next ID: %d
", r.invoices.nextID())
}

Test
Next ID: 2
Test2
Next ID: 3
Test3
Next ID: 4

Program exited.

Kudos to @dm03514 for mentioning "Type assertions" https://tour.golang.org/methods/15