2015-07-18 4 views
0

У меня есть следующий кодкарта метод указатель на интерфейс приемника

http://play.golang.org/p/d-bZxL72az

package main 

import "fmt" 

type Variables struct { 
    sum  uint64 
    highest uint64 
} 

type Data struct { 
    count uint64 
    mValue map[string]Variables 
} 

func (v Variables) Add(value Variables) Variables { 
    v.sum += value.sum 
    if v.highest == 0 { 
     v.highest = value.highest 
    } else if v.highest < value.highest { 
     v.highest = value.highest 
    } 
    return v 
} 

func (v *Variables) AddPointer(value Variables) { 
    v.sum += value.sum 
    if v.highest == 0 { 
     v.highest = value.highest 
    } else if v.highest < value.highest { 
     v.highest = value.highest 
    } 
} 

func main() { 
    var instances [2]Variables 
    instances[0] = Variables{sum: 5, highest: 3} 
    instances[1] = Variables{sum: 10, highest: 2} 
    var d Data 
    d.mValue = make(map[string]Variables) 
    for i:= 0; i < len(instances); i++ { 
     d.mValue["one"] = d.mValue["one"].Add(instances[i]) 
     d.mValue["two"].AddPointer(instances[i]) 
    } 
    fmt.Println(d.mValue["one"]) 
    fmt.Println(d.mValue["two"]) 
} 

Я получаю ошибку

# command-line-arguments 
/tmp/sandbox209565070/main.go:42: cannot call pointer method on d.mValue["two"] 
/tmp/sandbox209565070/main.go:42: cannot take the address of d.mValue["two"] 

(я думаю) Я понимаю вторую ошибку cannot take address - потому что, это карта, она не может принять адрес (это правильно?)

Это та же причина для первой ошибки (не может вызвать метод указателя)?

Есть ли способ использовать методы указателей на структуры, которые находятся в пределах карты ..

ответ

2

Да, тот же самая причина. Чтобы вызвать метод с приемником указателя, вам нужно либо иметь указатель в первую очередь, либо вам понадобится адресуемое значение, и Go автоматически примет указатель для вас.

Что вы можете сделать, то сделать mValue a map[string]*Variables вместо map[string]Variables. Затем вы будете хранить указатель на уже выделенный, гарантированный адрес Variables на карте, и вы сможете вызвать методы на этом указателе.

1

Чтобы расширить предыдущий ответ ...

На практике это обычно не проблема. Если тип имеет больше смысла без указателей (например, небольшая структура, где семантика значения имеет больше смысла), то у вас не будет приемников указателей, и проблема не возникнет.

Если приемники указателей имеют смысл, то вы, вероятно, должны использовать указатели на тип в большинстве мест, например, на картах (как hobbs said), и у вас не было бы методов, которые принимали бы не указательные аргументы или возвращали значения без указателя (приемники без указателей могут иметь смысл и будут просты в использовании). Опять же, вопрос не возникнет.

В первом случае, если вы хотите использовать приемник указателя с записью карты не указателя, вы можете использовать temporary (addressable) variable и переназначить его обратно на карту.

x := d.mValue["two"] 
    x.AddPointer(instances[i]) 
    // AddPointer uses a pointer receiver; `x` needs to be addressable, 
    // it will contain a copy of the value from the map and that copy may 
    // be changed by the method so we need to copy the new version back 
    // into the map. 
    d.mValue["two"] = x 

Во втором случае возникает несколько вопросов. Во-первых, чтобы избежать указателей nil, вам нужно либо инициализировать записи в карте, либо проверить на nil/existance на карте (или заставить методы приемника указателя обрабатывать nil оцененных приемников, но это не помогает при использовании методов, отличных от указателей) , Во-вторых, если по какой-то глупой причине у вас есть указатели, но у вас есть метод, который возвращает не указатель, вам придется использовать другой синтаксис для назначения карте.

Something like this perhaps:

// Initialize some map entries to avoid nil pointers 
    d.mValue = map[string]*Variables{ 
     "one": &Variables{}, 
     "two": &Variables{}, 
    } 
    for i := 0; i < len(instances); i++ { 
     // Just calling the non-pointer reciever is easy/fine: 
     d.mValue["one"].Add(instances[i]) 

     // But you can't do this: 
     //d.mValue["one"] = d.mValue["one"].Add(instances[i]) 
     // cannot use d.mValue["one"].Add(instances[i]) (type Variables) as type *Variables in assignment 

     *d.mValue["one"] = d.mValue["one"].Add(instances[i]) 
     d.mValue["two"].AddPointer(instances[i]) 
    } 
Смежные вопросы