Как установить значения по умолчанию в структурах Go

143

Есть несколько ответов / методов на следующий вопрос:

  1. Как установить значения по умолчанию для структур Голанга?
  2. Как инициализировать структуры в Голанге

У меня есть пара ответов, но требуется дальнейшее обсуждение.

Prateek
источник
@icza Ваш ответ дает способ сделать это, но, следуя названию вопроса, он ни в коем случае не похож и не доступен для поиска, поскольку это очень специфический вопрос. Я добавлю ссылку в моем ответе, хотя.
Prateek
Здесь есть два вопроса, выберите один. Предполагая, что вы выберете первый вопрос (в соответствии с названием вопроса), пожалуйста, будьте более конкретны в отношении вашего предшествующего исследования и тех случаев, когда другие ваши ответы требуют дополнительного обсуждения.
Дункан Джонс

Ответы:

96

Одна из возможных идей - написать отдельную функцию конструктора.

//Something is the structure we work with
type Something struct {
     Text string 
     DefaultText string 
} 
// NewSomething create new instance of Something
func NewSomething(text string) Something {
   something := Something{}
   something.Text = text
   something.DefaultText = "default text"
   return something
}
vodolaz095
источник
6
Да, это один из способов, которые я также упомянул в своем ответе, но мы никоим образом не можем заставить кого-либо использовать только эту функцию.
Prateek
@Prateek это либо это, либо использовать интерфейс, который был бы уродливым и слишком сложным.
OneOfOne
31
@Prateek Да, вы можете заставить людей использовать этот конструктор, если вы просто не экспортируете сам тип. Вы можете экспортировать функцию NewSomethingи даже поля Textи DefaultText, но только не экспортируйте тип структуры something.
Амит Кумар Гупта
1
Проблема усугубляется ... если для создания вашей структуры (например, через) используется сторонняя организация (например, библиотека reflect.New()), нельзя ожидать, что она будет знать о вашей специально названной фабричной функции. В этом случае, если не считать самого языка, думаю, подойдет только интерфейс (который может проверять библиотека).
Эдам
1
Хорошо установить значение по умолчанию, но иногда я могу захотеть переопределить значение по умолчанию. В этом случае я не смогу инициализировать структуру со значением, которое не является значением по умолчанию. немного раздражает меня
Juliatzin
68
  1. Вынудите метод получить структуру (путь конструктора).

    Из этого поста :

    Хороший дизайн - сделать ваш тип неэкспортированным, но предоставить экспортированную функцию конструктора, NewMyType()в которой вы можете правильно инициализировать свою структуру / тип. Также возвращайте тип интерфейса, а не конкретный тип, и интерфейс должен содержать все, что другие хотят сделать с вашим значением. И ваш конкретный тип должен реализовывать этот интерфейс, конечно.

    Это можно сделать, просто сделав сам тип неэкспортированным. Вы можете экспортировать функцию NewSomething и даже поля Text и DefaultText, но просто не экспортируйте что-нибудь типа структуры.

  2. Другой способ настроить его для собственного модуля - использовать структуру Config для установки значений по умолчанию (опция 5 в ссылке). Не очень хороший способ.

Prateek
источник
7
Теперь это неработающая ссылка (404): joneisen.tumblr.com/post/53695478114/golang-and-default-values
Виктор Заманян,
3
Это доступно в машине обратного хода .
n8henrie
FWIW, я думаю, что это «Вариант 3» - по крайней мере, в обратной связи машины. (Там нет «Вариант 5», там).
Decimus Phostle
33

Одна из проблем с вариантом 1 в ответе Виктора Заманяна состоит в том, что если тип не экспортируется, то пользователи вашего пакета не могут объявить его как тип для параметров функции и т. Д. Одним из способов решения этой проблемы является экспорт интерфейса вместо структура например

package candidate
// Exporting interface instead of struct
type Candidate interface {}
// Struct is not exported
type candidate struct {
    Name string
    Votes uint32 // Defaults to 0
}
// We are forced to call the constructor to get an instance of candidate
func New(name string) Candidate {
    return candidate{name, 0}  // enforce the default value here
}

Что позволяет нам объявлять типы параметров функции, используя экспортированный интерфейс кандидата. Единственный недостаток, который я вижу в этом решении, заключается в том, что все наши методы должны быть объявлены в определении интерфейса, но вы можете утверждать, что в любом случае это хорошая практика.

wolfson109
источник
Хороший простой пример.
13

Есть способ сделать это с тегами, который допускает несколько значений по умолчанию.

Предположим, у вас есть следующая структура с 2 тегами по умолчанию default0 и default1 .

type A struct {
   I int    `default0:"3" default1:"42"`
   S string `default0:"Some String..." default1:"Some Other String..."`
}

Теперь можно установить значения по умолчанию.

func main() {

ptr := &A{}

Set(ptr, "default0")
fmt.Printf("ptr.I=%d ptr.S=%s\n", ptr.I, ptr.S)
// ptr.I=3 ptr.S=Some String...

Set(ptr, "default1")
fmt.Printf("ptr.I=%d ptr.S=%s\n", ptr.I, ptr.S)
// ptr.I=42 ptr.S=Some Other String...
}

Вот полная программа на детской площадке .

Если вас интересует более сложный пример, например, с фрагментами и картами, взгляните на creasty / defaultse

Майк Кирико
источник
Большое спасибо! Я начал писать тот же код, который предлагал библиотека, и наткнулся на этот пост. Он делает именно то, что вы ожидаете ( github.com/creasty/defaults ). Если у вас нет значения, он устанавливает значение по умолчанию, но если вы присвоили значение своей переменной, он не будет назначать значение по умолчанию. Он очень хорошо работает с библиотекой yaml.v2.
Nordés
3

С https://golang.org/doc/effective_go.html#composite_literals :

Иногда нулевое значение недостаточно, и необходим инициализирующий конструктор, как в этом примере, полученном из пакета os.

    func NewFile(fd int, name string) *File {
      if fd < 0 {
        return nil
      }
      f := new(File)
      f.fd = fd
      f.name = name
      f.dirinfo = nil
      f.nepipe = 0
      return f
}
RDB
источник
-3
type Config struct {
    AWSRegion                               string `default:"us-west-2"`
}
user10209901
источник
1
Это неверно В лучшем случае вы можете установить значение тега в этом поле, а затем получить его значение с отражением, но даже при этом синтаксис является неправильным (отсутствуют обратные тики), и вы сможете установить значение по умолчанию только для строкового типа. Если у вас есть некоторое представление о том, на что конкретно ссылается этот пример, пожалуйста, добавьте ссылку для ссылки.
markeissler