Cara mengatur nilai default di Go struct

143

Ada beberapa jawaban / teknik untuk pertanyaan di bawah ini:

  1. Bagaimana cara mengatur nilai default ke struct golang?
  2. Cara menginisialisasi struct di golang

Saya punya beberapa jawaban tetapi diskusi lebih lanjut diperlukan.

Prateek
sumber
@icza Anda menjawab memang memberi cara untuk melakukannya tetapi dengan Judul Pertanyaan, sama sekali tidak mirip atau dicari karena itu adalah pertanyaan yang sangat spesifik. Saya akan menambahkan tautan dalam jawaban saya.
Prateek
Ada dua pertanyaan di sini, pilih satu. Dengan asumsi Anda memilih pertanyaan pertama (sesuai judul pertanyaan), harap lebih spesifik tentang penelitian Anda sebelumnya dan di mana jawaban Anda yang lain memerlukan lebih banyak diskusi.
Duncan Jones

Jawaban:

96

Satu ide yang mungkin adalah menulis fungsi konstruktor yang terpisah

//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
sumber
6
Ya, ini adalah salah satu cara yang juga saya sebutkan dalam jawaban saya tetapi tidak ada cara kita bisa memaksa siapa pun untuk menggunakan fungsi ini saja.
Prateek
@Prateek baik ini atau menggunakan antarmuka, yang akan jelek dan rumit.
OneOfOne
31
@Prateek ya, Anda dapat memaksa orang untuk menggunakan konstruktor ini jika Anda hanya membuat tipe itu sendiri tidak diekspor. Anda dapat mengekspor fungsi NewSomethingdan bahkan bidang Textdan DefaultText, tapi jangan ekspor jenis struct something.
Amit Kumar Gupta
1
Masalahnya lebih buruk ... jika pihak ketiga (perpustakaan, misalnya) digunakan untuk instantiate struct Anda (via reflect.New(), misalnya), itu tidak bisa diharapkan untuk mengetahui tentang fungsi pabrik Anda yang bernama khusus. Dalam hal itu, dan pendeknya bahasa itu sendiri sedang diubah, hanya antarmuka (yang perpustakaan dapat memeriksa) akan melakukan, saya pikir.
edam
1
Baik untuk mengatur default, tetapi kadang-kadang, saya mungkin ingin mengganti default. Dalam hal ini, saya tidak akan dapat menginisialisasi struct dengan nilai yang bukan default. sedikit mengganggu bagi saya
Juliatzin
68
  1. Paksa metode untuk mendapatkan struct (cara konstruktor).

    Desain yang bagus adalah membuat tipe Anda tidak diekspor, tetapi menyediakan fungsi konstruktor yang diekspor seperti NewMyType () di mana Anda dapat menginisialisasi struct / tipe Anda dengan benar. Kembalikan juga tipe antarmuka dan bukan tipe konkret, dan antarmuka tersebut harus berisi semua hal yang ingin dilakukan orang lain dengan nilai Anda. Dan tipe konkret Anda harus mengimplementasikan antarmuka itu tentu saja.

    Ini dapat dilakukan dengan hanya membuat tipe itu sendiri tidak diekspor. Anda dapat mengekspor fungsi NewSomething dan bahkan bidang Teks dan DefaultText, tetapi jangan ekspor sesuatu jenis struct

  2. Cara lain untuk menyesuaikannya untuk modul Anda sendiri adalah dengan menggunakan struct Config untuk menetapkan nilai default (Opsi 5 di tautan) Namun bukan cara yang baik.

Prateek
sumber
7
Ini sekarang merupakan tautan yang terputus (404): joneisen.tumblr.com/post/53695478114/golang-and-default-values
Victor Zamanian
3
Ini tersedia di mesin wayback .
n8henrie
FWIW, saya pikir itu adalah 'Opsi 3' - setidaknya di tautan mesin wayback. (Tidak ada 'Opsi 5', di sana).
decimus phostle
@ m90 untuk membungkam golint Anda dapat mendeklarasikan fungsi Anda sebagai mengembalikan jenis antarmuka publik
Thomas Grainger
@ThomasGrainger Komentar saya sepertinya merujuk pada revisi sebelumnya atas jawaban ini, tidak masuk akal lagi seperti ini :) Saya hanya akan menghapusnya.
m90
32

Satu masalah dengan opsi 1 dalam jawaban dari Victor Zamanian adalah bahwa jika tipe tidak diekspor maka pengguna paket Anda tidak dapat mendeklarasikannya sebagai tipe untuk parameter fungsi dll. Salah satu cara untuk mengatasi ini adalah dengan mengekspor antarmuka bukan misalnya struct

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
}

Yang memungkinkan kita mendeklarasikan tipe parameter fungsi menggunakan antarmuka Kandidat yang diekspor. Satu-satunya kelemahan saya dapat melihat dari solusi ini adalah bahwa semua metode kami perlu dideklarasikan dalam definisi antarmuka, tetapi Anda bisa berpendapat bahwa itu adalah praktik yang baik.

wolfson109
sumber
tersedia untuk mengubah variabel Nama dan Suara setelah panggilan Fungsi baru?
morteza khadem
Contoh sederhana yang bagus.
kesalahan ketik kecil: Votes unit32mungkin seharusnyaVotes uint32
PartyLich
@PartyLich terlihat dengan baik. Harus diperbaiki.
wolfson109
13

Ada cara untuk melakukan ini dengan tag, yang memungkinkan untuk beberapa default.

Asumsikan Anda memiliki struct berikut, dengan 2 tag default default0 dan default1 .

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

Sekarang mungkin untuk Mengatur default.

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

Inilah program lengkapnya di taman bermain .

Jika Anda tertarik pada contoh yang lebih kompleks, katakan dengan irisan dan peta, lalu, lihat creasty / defaultse

Mike Chirico
sumber
Terima kasih banyak! Saya mulai menulis kode yang sama seperti yang disarankan perpustakaan dan menemukan posting ini. Ia melakukan persis seperti yang Anda harapkan ( github.com/creasty/defaults ). Jika Anda tidak memiliki nilai itu menetapkan default, tetapi jika Anda menetapkan nilai ke variabel Anda, maka itu tidak akan menetapkan default. Ini berfungsi cukup baik dengan perpustakaan yaml.v2.
Nord
3

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

Terkadang nilai nol tidak cukup baik dan konstruktor inisialisasi diperlukan, seperti dalam contoh ini berasal dari paket 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
sumber
-3
type Config struct {
    AWSRegion                               string `default:"us-west-2"`
}
pengguna10209901
sumber
1
Ini salah. Paling-paling, Anda bisa menetapkan nilai tag pada bidang itu dan kemudian mendapatkan nilainya dengan refleksi tetapi bahkan dengan ini sintaksinya tidak benar (tidak ada tanda centang) dan Anda hanya dapat menetapkan nilai default untuk jenis string. Jika Anda memiliki wawasan tentang apa yang dimaksud contoh ini secara spesifik, harap tambahkan tautan untuk merujuk.
markeissler