Як встановити значення за замовчуванням у структурах Go


143

На наступне питання є кілька відповідей / методів:

  1. Як встановити значення за замовчуванням для голанг-структур?
  2. Як ініціалізувати структури в голангу

У мене є пара відповідей, але подальше обговорення потрібно.



@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
}

6
Так, це один із способів, про які я також згадував у своїй відповіді, але немає жодного способу змусити когось використовувати лише цю функцію.
Prateek

@Prateek це або це, або використовувати інтерфейс, який був би некрасивим і надскладним.
OneOfOne

31
@Prateek так, ви можете змусити людей використовувати цей конструктор, якщо ви просто зробите сам тип неекспортованим. Ви можете експортувати функцію NewSomethingта навіть поля Textта DefaultText, але просто не експортувати тип структури something.
Аміт Кумар Гупта

1
Проблема ще гірша ... якщо третя сторона (наприклад, бібліотека) використовується для екземпляру вашої структури ( reflect.New()наприклад, наприклад), не можна було б очікувати, що ви знаєте про вашу спеціально названу заводську функцію. У такому випадку, якщо не змінювати мову, міг би вважатись лише інтерфейс (який би могла перевірити бібліотека).
edam

1
Добре встановити за замовчуванням, але іноді, можливо, я хочу змінити типовий параметр. У цьому випадку я не зможу ініціалізувати структуру зі значенням, яке не є типовим. трохи прикро для мене
Джуліацін

68
  1. Примусовий метод отримати структуру (конструкторський шлях).

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

    Це можна зробити, просто зробивши сам тип не експортованим. Ви можете експортувати функцію NewSomething і навіть поля Text та DefaultText, але просто не експортуйте структуру

  2. Інший спосіб налаштувати його під власний модуль - це використовувати структуру Config для встановлення значень за замовчуванням (Варіант 5 за посиланням).


7
Зараз це ламане
Victor Zamanian

3
Він доступний у зворотній машині .
n8henrie

FWIW, я думаю, що це "Варіант 3" - принаймні, у зворотному зв'язку машини. (Там немає "Варіант 5".
децимус фостел

@ m90 щоб замовкнути golint, ви можете оголосити свою функцію повернення типу загальнодоступного інтерфейсу
Thomas Grainger

@ThomasGrainger Мій коментар, схоже, посилається на попередню редакцію цієї відповіді, вона насправді не має жодного сенсу, як це :) Я просто видалю її.
m90,

32

Однією з проблем Віктора Заманяна з варіантом 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
}

Що дозволяє нам оголошувати типи параметрів функції за допомогою експортованого інтерфейсу Candidate. Єдиний недолік, який я бачу в цьому рішенні, - це те, що всі наші методи потрібно задекларувати у визначенні інтерфейсу, але ви можете стверджувати, що це все-таки є хорошою практикою.


можна змінити ім'я та змінну голосів після виклику Нова функція?
мортеза хадем

Гарний простий приклад.

невеликий помилок: Votes unit32мабуть, має бутиVotes uint32
PartyLich

@PartyLich добре помічений. Слід виправити.
wolfson109

13

Існує спосіб зробити це за допомогою тегів, який дозволяє зробити кілька за замовчуванням.

Припустимо, у вас є така структура з двома тегами за замовчуванням 0 та 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...
}

Ось повна програма на дитячому майданчику .

Якщо вас цікавить складніший приклад, скажімо, з фрагментами та картами, тоді погляньте на кресті / defaultse


Дуже дякую! Я почав писати той самий код, що і бібліотека запропонував, і натрапив на цю публікацію. Це робить саме те, що ви очікуєте ( github.com/creasty/defaults ). Якщо у вас немає значення, воно встановлюється за замовчуванням, але якщо ви присвоїли значення своїй змінній, воно не призначатиме за замовчуванням. Він досить добре працює з бібліотекою yaml.v2.
Нордес

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
}

-3
type Config struct {
    AWSRegion                               string `default:"us-west-2"`
}

1
Це неправильно. У кращому випадку, ви можете встановити значення тега для цього поля, а потім дійти до його значення з відображенням, але навіть при цьому синтаксис є неправильним (відсутні типові тики), і ви зможете встановити лише значення за замовчуванням для типу рядка. Якщо у вас є деяке розуміння того, на що конкретно йдеться у цьому прикладі, будь ласка, додайте посилання, на яке слід посилатися.
markeissler
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.