Подтвердить что ты не робот

Golang указатели на указатели в качестве параметров функции

У меня есть функция

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

//if (complicated thing) add Cat to m

}

Верно ли, что m, рассматривает и собака передается по ссылке, а meowId имеет значение, скопированное значение.

Так как m - это отображение, то его перекрестная ссылка.

Собака - это структура. Итак, я должен передать указатель, чтобы избежать копирования данных.

Set - это интерфейс, как определено здесь:

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

Установлен ли проход по значению?

4b9b3361

Ответ 1

Тип интерфейса - это просто набор методов. Обратите внимание, что члены определения интерфейса не указывают, является ли тип приемника указателем. Это связано с тем, что набор методов типа значения является подмножеством набора методов связанного с ним типа указателя. Это глоток. Я имею в виду, если у вас есть следующее:

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

Ответ 2

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

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

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

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

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

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

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

Ответ 3

Вызовы, Спецификация языка программирования Go

В вызове функции значение функции и аргументы вычисляются в обычный порядок. После их оценки параметры вызова передаются по значению функции, и вызываемая функция начинается выполнение. Возвращаемые параметры функции передаются по значению вернуться к вызывающей функции, когда функция вернется.

Когда параметры функции передаются по значению? FAQ - The Go Язык программирования.

Как и во всех языках семейства C, все в Go передается по значению. То есть, функция всегда получает копия передаваемой вещи, как если бы было задание оператор присваивает значение параметру. Например, передача значение int для функции делает копию int и передает значение указателя делает копию указателя, но не данные, которые он указывает к. (См. Следующий раздел для обсуждения того, как это влияет на метод приемники.)

Значения карты и среза ведут себя как указатели: они являются дескрипторами, которые содержат указатели на базовую карту или данные среза. Копирование карты или Значение среза не копирует данные, на которые указывает. Копирование интерфейса value делает копию вещи, хранящейся в значении интерфейса. Если значение интерфейса содержит структуру, копирование значения интерфейса делает копия структуры. Если значение интерфейса содержит указатель, копирование значение интерфейса делает копию указателя, но опять же не данные, на которые он указывает.