Какие типы являются изменяемыми и неизменяемыми в языке Google Go?

В Google Go я читал, что строки неизменяемы, но это целые числа? А как насчет других типов? Как программист чуть старше меня, я предпочитаю изменчивость, хотя я знаю о преимуществах неизменности, я предпочитаю жить опасно.

Было бы очень полезно знать, какие типы являются изменяемыми или неизменяемыми.


Обновление, что меня больше всего беспокоит, так это практические вопросы, зависящие от того, является ли тип изменяемым или неизменным. Как и в типичном примере на Java, если вы создадите String в цикле и в цикле 10 000 раз, вы получите 10 000 String, которые затем будут собраны в мусор. На самом деле это было серьезной проблемой в проекте в компании, в которой я работал.

Возникает вопрос, вызывает ли в некоторых случаях неизменяемость Go ту же проблему?

Это влияет на то, как вы должны обращаться с вар. (или я предполагаю, что это так).


Обновите еще раз, меня также беспокоят другие практические проблемы. Знание того, что что-то является неизменным, означает, что я могу писать код, который является параллельным, и обновления одной ссылки объекта не должны обновлять другие ссылки. Однако иногда я хочу делать опасные вещи, мне нужна изменчивость.

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


person Phil    schedule 05.11.2011    source источник
comment
Причина, по которой я спрашиваю, заключается в том, что это проблема производительности. Если я пишу цикл, который повторяется миллион раз, мне не нужно создавать много объектов. Например, в цикле Java мы не хотим создавать строку при каждом обходе цикла, поэтому мы используем StringBuffer.   -  person Phil    schedule 05.11.2011
comment
Я, конечно, предполагаю, что неизменяемые типы в целом будут создавать множество объектов. (может быть неправдой, в зависимости от распределителя)   -  person Phil    schedule 05.11.2011
comment
в некотором смысле правильно, потому что чрезвычайно умный сборщик мусора мог бы повторно использовать оставленные вами строковые фрагменты с помощью некоторой умной (что угодно, кроме непрерывного массива слов). тогда используйте []byte, будьте осторожны, так как полноразмерный символ utf-8 (он же руна) имеет переменную длину.   -  person ypb    schedule 05.11.2011


Ответы (5)


Не волнуйтесь - Go позволит вам выстрелить себе в ногу, если вы действительно этого хотите :-)

Go не похож на Erlang, и это может быть то, что вы имеете в виду, задавая вопрос.

x := 1
x = 2

выделяет одну переменную x со значением 1, затем переназначает ее на 2 - здесь не выделяется дополнительная память.

Как вы заметили, строки неизменяемы, поэтому манипуляции со строками могут привести к созданию копий. Если вы обнаружите, что хотите внести изменения в символьные данные на месте, вы, вероятно, захотите работать с переменными []byte через пакет bytes.

Сообщение Расса Кокса об этом должно ответить на большинство ваших вопросов об основных структурах данных: http://research.swtch.com/2009/11/go-data-structures.html

Как отмечали другие комментаторы, вам стоит взглянуть на семантику значений функций Go - поначалу они могут показаться немного неожиданными.

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

func (t MyType) myFunc() {
    // do something to set a field in t
}

и вы звоните в свой код

myVar.myFunc()

вы можете быть удивлены, увидев, что это не делает то, что вы хотите, потому что t, который виден в myFunc(), на самом деле является копией myVar.

Но следующее будет работать:

func (t *myType) myFunc() {
    // do something to set a field in t
}

потому что функция имеет копию указателя и может обращаться к базовой структуре через этот указатель.

person robothor    schedule 05.11.2011
comment
Спасибо! Это была именно та информация, которая мне нужна для отладки проблемы с отсутствующей звездочкой в ​​сигнатуре метода. Для меня удивительно, что Го так легко позволяет тебе прострелить себе ногу в этом отношении, когда он так противно громко говорит о том, чтобы помочь тебе другими способами. - person Craig Jones; 26.09.2014
comment
@robothor благодарим вас за то, что вы указали разницу между получателем как T и * T; это только позволило мне закончить дневную охоту за ошибками. Как вы узнали об этом? Я надеюсь, что мне не хватает хорошего справочника по языку, потому что я только что проследил спецификацию Go и Effective Go и ... да, эта информация есть, но только если вы точно знаете, что искать! - person hmijail mourns resignees; 13.10.2020

На мой взгляд, прежде всего следует разделить следующие два понятия:

  • целые числа как математические объекты (то есть: значения)

  • переменные типа int

Тогда ответ таков: Целочисленные переменные изменяемы, целые значения неизменны.

Это представление согласуется со спецификацией Go, в которой говорится, что строки неизменяемы. Очевидно, строковая переменная изменяема.

Переменные (как концепция) в Go как минимум:

  • именованные переменные (например: var i int)
  • переменные, доступные через указатели

Объекты Mutable Go:

  • массивы и срезы
  • карты
  • каналы
  • замыкания, которые захватывают как минимум 1 переменную из внешней области видимости

Неизменяемые объекты Go:

  • интерфейсы
  • логические, числовые значения (включая значения типа int)
  • струны
  • указатели
  • указатели на функции и замыкания, которые можно свести к указателям на функции
  • структуры с одним полем

Go объекты, которые некоторые люди могут считать изменяемыми, в то время как другие могут считать их неизменными:

  • структуры с несколькими полями
person Community    schedule 05.11.2011
comment
struct определенно является изменяемым типом, но мы этого не чувствуем, потому что он всегда копируется при передаче. Таким образом, мутация не распространяется. - person eonil; 25.10.2013

Да, слово неизменяемый встречается в спецификации Go ровно один раз. И это при обсуждении type string. Я думаю, вам следует взглянуть на это с двух точек зрения: назначаемости и Адресуемость. Например, Go, очевидно, запретит вам повторно связывать переменную с другим значением типа с неэкспортированными свойствами. Вроде как в C ++ для классов, не предоставляющих конструктор копирования, но в Go Pimpl гораздо менее неудобно, соответствует общему содержанию философии горутин.

person ypb    schedule 05.11.2011
comment
Да, но если у меня есть int, и я устанавливаю его значение, и я создаю новый экземпляр int (неизменяемый) или устанавливаю его значение в памяти (перезапись, изменяемый). В качестве альтернативы, защищен ли я каким-то образом от того, чтобы даже знать, так ли это, и это зависит от реализации Go? хм, может, я смогу придумать, как это проверить, не уверен. - person Phil; 05.11.2011
comment
package main import fmt func main () {var i int i = 5 fmt.Println (& i) i = 6 fmt.Println (& i) var k = 7 i = k fmt.Println (& i)} - person Phil; 05.11.2011
comment
@ Фил не уверен, что тебе сказать. возможно, только Go передается по значению, поэтому вы должны явно указать адрес объекта, а единственный тип ссылки - это нетипизированный интерфейс {}. / хммм ... не особо учитель, мои;} - person ypb; 05.11.2011
comment
@Phil 1 - целое число - это значение. Это означает, что переменная является экземпляром. Он представляет собой фактическое место в памяти, где хранится int, а не ссылку на него. Таким образом, присвоение переменной не может создать новый экземпляр. Чтобы доказать это, обратите внимание, что адрес переменной не меняется после того, как она была назначена. - person SteveMcQwark; 06.11.2011

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

Для int на самом деле не имеет смысла говорить об изменчивости, потому что каковы «компоненты» int? Вы изменяете int, присваивая целиком новый int, но присвоение не считается "изменением".

Существует некоторая связь между проблемами изменчивости и типов ссылок и значений. Семантически нет разницы между неизменяемым ссылочным типом и типом значения. Почему? Предположим, что int на самом деле был указателем на неизменяемый объект (т.е. *InternalIntObject без функций для изменения InternalIntObject). После того, как вы назначите такой указатель переменной, он всегда будет представлять одно и то же целочисленное значение (не может быть изменено другими, которые используют тот же объект), поскольку объект неизменяем. Это то же поведение, что и у целочисленного типа значения. Вы можете назначить целые числа с помощью оператора присваивания; аналогично вы можете назначить эти указатели путем присваивания; результат будет таким же: присвоенная переменная представляет то же целое число, что и присвоенное ей. Единственная разница будет заключаться в том, что сравнение и арифметические операторы необходимо будет переопределить, чтобы отменить ссылку на указатель для вычисления результата.

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

Что касается того, что вы спросили, «изменяемые» типы обычно считаются ссылочными типами, кроме строковых: карты, каналы, срезы (по отношению к данным, на которые указывает срез), а также указатели на что угодно (поскольку вы можете изменять значение в месте, указанном указателем).

person newacct    schedule 05.11.2011
comment
О вашем предложении. Проверка того, является ли что-то изменчивым, заключается в том, если вы дадите эту вещь кому-то, можете ли вы изменить какой-то аспект этого ...: Я думаю, что то, что вы написали, является действительной точкой зрения. Но мне кажется, что лучше (с моей точки зрения) думать об изменчивости с точки зрения именования и композиции / перекрытия. То есть, как получить имя объекта, изменить объект и какие другие именованные объекты будут изменены вследствие этого. Имя здесь - вещь концептуальная. - person ; 06.11.2011
comment
хорошо, я удалил этот раздел, так как понимаю, что на самом деле речь идет о ссылочных типах - person newacct; 07.11.2011

Кажется, ваша забота больше связана с распределением, чем неизменностью. Неизменяемость, безусловно, влияет на распределение, делая невозможным повторное использование памяти. Умный компилятор мог бы повторно использовать любую «неизменяемую» память, адрес которой, как ему известно, не ускользнул.

Помимо строк, будьте осторожны с интерфейсами. Все, что больше размера слова, должно быть выделено при назначении интерфейсу (не считая оптимизаций). Кроме того, переменные, объявленные в теле цикла, адреса которых уходят, в том числе через замыкание, должны будут выделяться каждый раз в цикле. Однако в противном случае задание - это просто задание. Значение просто копируется в память, представленную переменной.

Если вы используете make или new в цикле или любой литерал, который создает ссылку, выделение должно произойти (опять же, при условии оптимизации).

По сути, все сводится к тому, чтобы попытаться повторно использовать память там, где это возможно, и надеяться, что компилятор сделает это за вас, когда вы не можете, если это имеет смысл.

person SteveMcQwark    schedule 06.11.2011