Вопрос по pointers, go – указатели Голанга на указатели как параметры функции

20

У меня есть следующая функция:

func addCatsToMap(m map[string][]CatHouse, meowId int, treats Set, dog *Dog) {

//if (complicated thing) add Cat to m

}

гдеSet, типtreats, является интерфейсом со следующим определением:

type Set interface {
  Add(value string)
  Contains(value string) (bool)
  Length() (int)
  RemoveDuplicates()
}

Question:

Это правда, чтоm, treats, а такжеdog являютсяpassed-by-reference, а такжеmeowId скопировано ли это значение?

Я предполагаю, что:

m is pass-by-reference because it's a map dog is a struct. So, I should pass the pointer to avoid copying the data

Ваш Ответ

3   ответа
3

Calls, The Go Programming Language Specification

In a function call, the function value and arguments are evaluated in the usual order. After they are evaluated, the parameters of the call are passed by value to the function and the called function begins execution. The return parameters of the function are passed by value back to the calling function when the function returns.

When are function parameters passed by value? FAQ - The Go Programming Language.

As in all languages in the C family, everything in Go is passed by value. That is, a function always gets a copy of the thing being passed, as if there were an assignment statement assigning the value to the parameter. For instance, passing an int value to a function makes a copy of the int, and passing a pointer value makes a copy of the pointer, but not the data it points to. (See the next section for a discussion of how this affects method receivers.)

Map and slice values behave like pointers: they are descriptors that contain pointers to the underlying map or slice data. Copying a map or slice value doesn't copy the data it points to. Copying an interface value makes a copy of the thing stored in the interface value. If the interface value holds a struct, copying the interface value makes a copy of the struct. If the interface value holds a pointer, copying the interface value makes a copy of the pointer, but again not the data it points to.

38

что члены определения интерфейса не указывают, является ли тип получателя указателем. Это потомуthe method set of a value type is a subset of the method set of its associated pointer type, Это полный рот. Я имею в виду, если у вас есть следующее:

type Whatever struct {
    Name string
}

и вы определяете следующие два метода:

func (w *Whatever) Foo() {
    ...
}

func (w Whatever) Bar() {
    ...
}

Тогда типWhatever имеет только методBar()в то время как тип*Whatever имеет методыFoo() а такжеBar(), Это означает, что если у вас есть следующий интерфейс:

type Grits interface {
    Foo()
    Bar()
}

затем*Whatever инвентарьGrits ноWhatever нет, потому чтоWhatever не хватает методаFoo(), Когда вы определяете входные данные для функции в качестве типа интерфейса, вы не представляете, является ли это указателем или типом значения.

В следующем примере показана функция, которая принимает тип интерфейса в обоих направлениях:

package main

import "fmt"

type Fruit struct {
    Name string
}

func (f Fruit) Rename(name string) {
    f.Name = name
}

type Candy struct {
    Name string
}

func (c *Candy) Rename(name string) {
    c.Name = name
}

type Renamable interface {
    Rename(string)
}

func Rename(v Renamable, name string) {
    v.Rename(name)
    // at this point, we don't know if v is a pointer type or not.
}

func main() {
    c := Candy{Name: "Snickers"}
    f := Fruit{Name: "Apple"}
    fmt.Println(f)
    fmt.Println(c)
    Rename(f, "Zemo Fruit")
    Rename(&c, "Zemo Bar")
    fmt.Println(f)
    fmt.Println(c)
}

ты можешь позвонитьRaname(&f, "Jorelli Fruit") но нетRename(c, "Jorelli Bar")потому что обаFruit а также*Fruit воплощать в жизньRenamable, в то время как*Candy инвентарьRenable а такжеCandy не.

http://play.golang.org/p/Fb-L8Bvuwj

6

в Go ничего не передается по ссылке. Передача по ссылке означает, что оператор присваивания может изменить исходное значение, если используется один. Однако есть ссылочные типы, такие как карты и указатели, которые указывают куда-то. Использование оператора присваивания для них не приведет к изменению оригинала, если вы не используете другие операторы, такие как индекс карты и* оператор.

Вы правы в том, что ваша картаm является ссылочным типом и, следовательно, как указатель. Любые изменения на карте, кроме замены карты, изменят оригинал.

m["whatever"] = 2           // Modifies the original map
m = anothermap              // Does not modify the original map

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

Передача указателя, как вы делаете сdog позволяет изменить оригинал. Если вы вызываете какие-либо методы указателя или используете* оператор, оригинал изменится. В вашем примере указатель может не понадобиться. ЕслиDog маленький, может быть проще просто передать копию. Программист должен определить, когда пора использовать указатель.

Set не передается по ссылке.Interfaces are not references. Хотя верно, что внутри компилятора 6g интерфейс использует указатели, сам интерфейс не действует как один. Передача интерфейса, независимо от размера объекта, который он содержит, такая же дешевая, как и передача указателя с помощью компилятора 6g. Тем не менее, нет способа изменить исходное значение интерфейса, как вы можете с помощью указателей и карт.

Хотя вы не можете изменить исходный переданный интерфейс, интерфейс может содержать тип указателя. В этом случае он будет действовать как указатель на собаку, когда вызов определенных методов может изменить оригинал. Для вашего конкретногоSet Интерфейс, я думаю, он содержит тип указателя на основе имен методов. Поэтому, когда вы звонитеset.Add(whatever), это изменит внутренние данные оригинала.

Похожие вопросы