Jak ustawić wartości domyślne w strukturach Go

143

Istnieje wiele odpowiedzi / technik na poniższe pytanie:

  1. Jak ustawić domyślne wartości struktur golang?
  2. Jak zainicjować struktury w golang

Mam kilka odpowiedzi, ale wymagana jest dalsza dyskusja.

Prateek
źródło
@icza You answer daje sposób na zrobienie tego, ale idąc według tytułu pytania, nie jest w żaden sposób podobny ani możliwy do wyszukania, ponieważ jest to bardzo konkretne pytanie. Dodam jednak link w mojej odpowiedzi.
Prateek
Są tu dwa pytania, wybierz jedno. Zakładając, że zdecydujesz się na pierwsze pytanie (zgodnie z tytułem pytania), podaj bardziej szczegółowe informacje na temat wcześniejszych badań i miejsc, w których inne odpowiedzi wymagają dalszej dyskusji.
Duncan Jones,

Odpowiedzi:

96

Jednym z możliwych pomysłów jest napisanie oddzielnej funkcji konstruktora

//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
źródło
6
Tak, jest to jeden ze sposobów, o których również wspomniałem w mojej odpowiedzi, ale w żaden sposób nie możemy nikogo zmusić do korzystania wyłącznie z tej funkcji.
Prateek
@Prateek to albo to, albo użyj interfejsu, który byłby brzydki i zbyt skomplikowany.
OneOfOne
31
@Prateek tak, możesz zmusić ludzi do używania tego konstruktora, jeśli po prostu sprawisz, że sam typ zostanie niewyeksportowany. Możesz wyeksportować funkcję, NewSomethinga nawet pola Texti DefaultText, ale po prostu nie eksportuj typu struktury something.
Amit Kumar Gupta
1
Problem jest gorszy ... jeśli strona trzecia (na przykład biblioteka) jest używana do tworzenia instancji twojej struktury ( reflect.New()na przykład przez), nie można oczekiwać, że dowie się o twojej specjalnie nazwanej funkcji fabryki. W takim przypadku, poza zmianą samego języka, wystarczyłby tylko interfejs (który biblioteka mogłaby sprawdzić).
edam
1
Dobrze jest ustawić domyślne, ale czasami mogę chcieć zastąpić domyślne. W tym przypadku nie będę mógł zainicjować struktury z wartością, która nie jest domyślna. trochę irytujące dla mnie
Juliatzin
68
  1. Wymuś metodę, aby uzyskać strukturę (sposób konstruktora).

    Dobrym projektem jest niewyeksportowanie typu, ale zapewnienie wyeksportowanej funkcji konstruktora, takiej jak NewMyType (), w której można poprawnie zainicjować strukturę / typ. Zwróć także typ interfejsu, a nie konkretny typ, a interfejs powinien zawierać wszystko, co inni chcą zrobić z twoją wartością. Twój konkretny typ musi oczywiście implementować ten interfejs.

    Można to zrobić, po prostu wyłączając sam typ. Możesz wyeksportować funkcję NewSomething a nawet pola Text i DefaultText, ale po prostu nie eksportuj struktury wpisz coś

  2. Innym sposobem dostosowania go do własnego modułu jest użycie struktury Config do ustawienia wartości domyślnych (opcja 5 w linku). Nie jest to jednak dobry sposób.

Prateek
źródło
7
To jest teraz uszkodzony link (404): joneisen.tumblr.com/post/53695478114/golang-and-default-values
Victor Zamanian
3
Jest dostępny w maszynie powrotnej .
n8henrie
FWIW, myślę, że to „opcja 3” - przynajmniej w połączeniu z maszyną powrotną. (Nie ma tam „Opcji 5”).
decimus phostle
@ m90, aby wyciszyć golinta, możesz zadeklarować swoją funkcję jako zwracającą typ interfejsu publicznego
Thomas Grainger
@ThomasGrainger Mój komentarz wydaje się odnosić do poprzedniej rewizji tej odpowiedzi, nie ma już takiego sensu :) Po prostu go usunę.
m90
32

Jednym z problemów z opcją 1 w odpowiedzi Victora Zamaniana jest to, że jeśli typ nie jest eksportowany, użytkownicy twojego pakietu nie mogą zadeklarować go jako typ parametrów funkcji itp. Jednym ze sposobów obejścia tego byłoby wyeksportowanie interfejsu zamiast struktura np

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
}

Dzięki temu możemy zadeklarować typy parametrów funkcji za pomocą wyeksportowanego interfejsu Kandydata. Jedyną wadą, jaką widzę w tym rozwiązaniu, jest to, że wszystkie nasze metody muszą być zadeklarowane w definicji interfejsu, ale można by argumentować, że i tak jest to dobra praktyka.

wolfson109
źródło
jest dostępna zmiana nazwy i głosów po wywołaniu Nowa funkcja?
morteza khadem
Ładny, prosty przykład.
mała literówka: Votes unit32prawdopodobnie powinno byćVotes uint32
PartyLich
@PartyLich dobrze zauważony. Powinien zostać naprawiony.
wolfson109
13

Można to zrobić za pomocą tagów, które pozwalają na wiele wartości domyślnych.

Załóżmy, że masz następującą strukturę z 2 domyślnymi tagami default0 i default1 .

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

Teraz można ustawić wartości domyślne.

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

Oto pełny program na placu zabaw .

Jeśli interesuje Cię bardziej złożony przykład, powiedzmy z plasterkami i mapami, spójrz na creasty / defaultse

Mike Chirico
źródło
Wielkie dzięki! Zacząłem pisać ten sam kod, który zasugerowała biblioteka i natknąłem się na ten post. Robi dokładnie to, czego oczekujesz ( github.com/creasty/defaults ). Jeśli nie masz żadnej wartości, ustawia wartość domyślną, ale jeśli przypisałeś wartość do zmiennej, nie przypisze wartości domyślnej. Działa całkiem dobrze z biblioteką yaml.v2.
Nordes
3

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

Czasami wartość zero nie jest wystarczająco dobra i potrzebny jest konstruktor inicjujący, jak w tym przykładzie pochodzącym z pakietu 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
źródło
-3
type Config struct {
    AWSRegion                               string `default:"us-west-2"`
}
user10209901
źródło
1
To jest niepoprawne. W najlepszym razie możesz ustawić wartość tagu w tym polu, a następnie uzyskać jego wartość z odbiciem, ale nawet w tym przypadku składnia jest niepoprawna (brak znaczników wstecznych) i możesz ustawić tylko domyślną wartość dla typu ciągu. Jeśli masz jakiś wgląd w to, do czego konkretnie odnosi się ten przykład, dodaj link, do którego się odnosi.
markeissler,