Como definir valores padrão nas estruturas Go

143

Existem várias respostas / técnicas para a pergunta abaixo:

  1. Como definir valores padrão para estruturas golang?
  2. Como inicializar estruturas em golang

Tenho algumas respostas, mas é necessária mais discussão.

Prateek
fonte
@icza Sua resposta fornece uma maneira de fazê-lo, mas seguindo o título da pergunta, não é de forma alguma semelhante ou pesquisável, pois é uma pergunta muito específica. Vou adicionar o link na minha resposta embora.
Prateek #
Há duas perguntas aqui, escolha uma. Supondo que você opte pela primeira pergunta (conforme o título da pergunta), seja mais específico sobre sua pesquisa anterior e onde suas outras respostas exigem mais discussão.
Duncan Jones

Respostas:

96

Uma idéia possível é escrever uma função construtora separada

//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
fonte
6
Sim, essa é uma das maneiras que eu também mencionei na minha resposta, mas não há como forçar alguém a usar apenas essa função.
Prateek #
O @Prateek é isso ou usa uma interface que seria feia e complicada demais.
OneOfOne
31
@ Prateek sim, você pode forçar as pessoas a usar esse construtor se você simplesmente tornar o tipo não importado. Você pode exportar a função NewSomethinge até os campos Texte DefaultText, mas não exporte o tipo de estrutura something.
Amit Kumar Gupta
1
O problema é pior ... se um terceiro (biblioteca, por exemplo) for usado para instanciar sua estrutura (via reflect.New(), por exemplo), não se pode esperar que você saiba sobre sua função de fábrica com nome especial. Nesse caso, e menos que a própria linguagem seja alterada, acho que apenas uma interface (que a biblioteca poderia verificar) faria.
Edam
1
É bom definir o padrão, mas às vezes eu posso substituir o padrão. Nesse caso, não poderei inicializar uma estrutura com um valor que não seja o padrão. um pouco chato para mim #
211 Juliatzin
68
  1. Forçar um método para obter a estrutura (a maneira do construtor).

    Um bom design é tornar seu tipo não exportado, mas fornecer uma função construtora exportada como NewMyType () na qual você pode inicializar corretamente sua estrutura / tipo. Também retorne um tipo de interface e não um tipo concreto, e a interface deve conter tudo o que os outros desejam fazer com seu valor. E seu tipo concreto deve implementar essa interface, é claro.

    Isso pode ser feito simplesmente tornando o tipo não importado. Você pode exportar a função NewSomething e até os campos Text e DefaultText, mas simplesmente não exporta o tipo de estrutura

  2. Outra maneira de personalizá-lo para o seu próprio módulo é usar uma estrutura Config para definir valores padrão (Opção 5 no link) Não é uma boa maneira.

Prateek
fonte
7
Este é agora um link quebrado (404): joneisen.tumblr.com/post/53695478114/golang-and-default-values
Victor Zamanian
3
Está disponível na máquina de wayback .
N8henrie 26/05
FWIW, acho que é a 'Opção 3' - pelo menos no link da máquina de wayback. (Não existe 'Opção 5').
Decimus phostle # 03
@ m90 para golint silêncio você pode declarar a sua função como retornando o tipo de interface pública
Thomas Grainger
@ThomasGrainger Meu comentário parece estar se referindo a uma revisão anterior desta resposta, ela realmente não faz mais sentido assim :) Vou apenas excluí-la.
M90
32

Um problema com a opção 1 na resposta de Victor Zamanian é que, se o tipo não for exportado, os usuários do seu pacote não poderão declará-lo como o tipo de parâmetros de função etc. Uma maneira de contornar isso seria exportar uma interface em vez de estrutura, por exemplo

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
}

O que nos permite declarar tipos de parâmetros de função usando a interface Candidate exportada. A única desvantagem que vejo desta solução é que todos os nossos métodos precisam ser declarados na definição da interface, mas você pode argumentar que isso é uma boa prática.

wolfson109
fonte
está disponível para alterar a variável Nome e Votos após a chamada Nova função?
morteza khadem 16/01/19
Belo exemplo simples.
pequeno erro de digitação: Votes unit32provavelmente deve serVotes uint32
PartyLich
@PartyLich bem visto. Deve ser consertado.
wolfson109
13

Existe uma maneira de fazer isso com as tags, que permitem vários padrões.

Suponha que você tenha a seguinte estrutura, com 2 tags padrão default0 e default1 .

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

Agora é possível definir os padrões.

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

Aqui está o programa completo em um playground .

Se você estiver interessado em um exemplo mais complexo, digamos, com fatias e mapas, dê uma olhada em creasty / defaultse

Mike Chirico
fonte
Muito obrigado! Comecei a escrever o mesmo código sugerido pela biblioteca e me deparei com este post. Ele faz exatamente o que você espera ( github.com/creasty/defaults ). Se você não tem valor, ele define o padrão, mas se você atribuiu um valor à sua variável, ele não atribui o padrão. Funciona muito bem com a biblioteca yaml.v2.
Nordes 04/02
3

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

Às vezes, o valor zero não é bom o suficiente e é necessário um construtor de inicialização, como neste exemplo, derivado do pacote 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
fonte
-3
type Config struct {
    AWSRegion                               string `default:"us-west-2"`
}
user10209901
fonte
1
Isto está incorreto. Na melhor das hipóteses, você pode definir um valor de tag nesse campo e, em seguida, chegar a seu valor com reflexão, mas mesmo com isso a sintaxe está incorreta (faltando ticks de retorno) e você só poderá definir um valor padrão para um tipo de string. Se você tiver alguma ideia sobre o que este exemplo está se referindo especificamente, adicione um link para se referir.
Markeissler 11/08/19