如何使用派生类型进行sync.Map

问题描述:

My goal is to have a custom type the one I could derive and add extra methods, for example, when using a map this works:

package main

import "fmt"

type myMap map[string]string

func (m *myMap) Add() {
    _, ok := (*m)["test"]
    println(ok)
}

func main() {
    x := &myMap{}
    fmt.Printf("x = %+v
", x)
}

But if want the same with sync.Map how to do it? I am currently trying this: https://play.golang.org/p/8PjpPY-Sjq

package main

import (
    "fmt"
    "sync"
)

type myMap sync.Map

func (m *myMap) Add() {
    _, ok := (*m).Load("test")
    println(ok)
}

func main() {
    x := &myMap{}
    fmt.Printf("x = %+v
", x)
}

But get this error:

(*m).Load undefined (type myMap has no field or method Load)

Any ideas?

The Go Programming Language Specification

Struct types

A struct is a sequence of named elements, called fields, each of which has a name and a type. Field names may be specified explicitly (IdentifierList) or implicitly (EmbeddedField). Within a struct, non-blank field names must be unique.

StructType    = "struct" "{" { FieldDecl ";" } "}" .
FieldDecl     = (IdentifierList Type | EmbeddedField) [ Tag ] .
EmbeddedField = [ "*" ] TypeName .
Tag           = string_lit .

A field declared with a type but no explicit field name is called an embedded field. An embedded field must be specified as a type name T or as a pointer to a non-interface type name *T, and T itself may not be a pointer type. The unqualified type name acts as the field name.

A field or method f of an embedded field in a struct x is called promoted if x.f is a legal selector that denotes that field or method f.

Promoted fields act like ordinary fields of a struct except that they cannot be used as field names in composite literals of the struct.

Given a struct type S and a type named T, promoted methods are included in the method set of the struct as follows:

  • If S contains an embedded field T, the method sets of S and *S both include promoted methods with receiver T. The method set of *S also includes promoted methods with receiver *T.

  • If S contains an embedded field *T, the method sets of S and *S both include promoted methods with receiver T or *T.


Use an embedded field of a struct. For example,

package main

import (
    "fmt"
    "sync"
)

type myMap struct {
    sync.Map
}

func (m *myMap) Add(key, value interface{}) bool {
    _, loaded := m.LoadOrStore(key, value)
    return !loaded
}

func main() {
    x := &myMap{}
    k := "test"
    ok := x.Add(k, 42)
    fmt.Println(ok)
    v, ok := x.Load(k)
    fmt.Println(k, v, ok)
}

Playground: https://play.golang.org/p/YCNeiYVhlT

Output:

true
test 42 true

The Go Programming Language Specification

Selectors

For a primary expression x that is not a package name, the selector expression

x.f

denotes the field or method f of the value x (or sometimes *x; see below). The identifier f is called the (field or method) selector; it must not be the blank identifier. The type of the selector expression is the type of f. If x is a package name, see the section on qualified identifiers.

A selector f may denote a field or method f of a type T, or it may refer to a field or method f of a nested embedded field of T. The number of embedded fields traversed to reach f is called its depth in T. The depth of a field or method f declared in T is zero. The depth of a field or method f declared in an embedded field A in T is the depth of f in A plus one.

Rule 1:

For a value x of type T or *T where T is not a pointer or interface type, x.f denotes the field or method at the shallowest depth in T where there is such an f. If there is not exactly one f with shallowest depth, the selector expression is illegal.


Sometimes, there is ambiguity in complex cases of nested embedding. If so, specify the full qualifiers explicitly. For example, m.Map.LoadOrStore and x.Map.Load.

package main

import (
    "fmt"
    "sync"
)

type myMap struct {
    sync.Map
}

func (m *myMap) Add(key, value interface{}) bool {
    _, loaded := m.Map.LoadOrStore(key, value)
    return !loaded
}

func main() {
    x := &myMap{}
    k := "test"
    ok := x.Add(k, 42)
    fmt.Println(ok)
    v, ok := x.Map.Load(k)
    fmt.Println(k, v, ok)
}

The line: type myMap sync.Map defines a new type based on an existing type.

According to the Language Specification:

A defined type may have methods associated with it. It does not inherit any methods bound to the given type

It means, that myMap only inherits the fields of sync.Map (which is a struct) but not it's methods. It doesn't work like class inheritance in other languages.

Specifically, sync.Map does not have any exported fields, so using it to define a new type is pointless. Instead you should define a struct type that contains sync.Map as a field. Then you'll have access to it's methods. @peterSO's answer shows how to do that.