Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Default Value #165

Closed
johscheuer opened this issue Mar 21, 2016 · 13 comments
Closed

Default Value #165

johscheuer opened this issue Mar 21, 2016 · 13 comments

Comments

@johscheuer
Copy link

Hello,

I wanted to ask if it's possible to redefine the zero values for int. For example I need something like this:

...
  # int32
  max: 2147483647
  min: -2147483648

If the value is set it should use the specified value and if there is no value specified it should use the defined zero/default value. Sadly I can't use the 0 as zero value because it is a valid value for min/max.

My current solution is to use a string instead of int.

@vincentbernat
Copy link

vincentbernat commented Jul 18, 2016

You can easily set default values if you provide a non-zero value:

type Stuff struct {
  Max int32
  Min int32
}

func Parse(in []byte) error {
  var stuff = Stuff{Max: 2147483647, Min: -147483648}
  if err := yaml.Unmarshal(in, &stuff); err != nil {
    return err
  }
  ...
  return nil
}

@vincentbernat
Copy link

Another option would be tu use:

type Stuff struct {
  Max *int32
  Min *int32
}

You can then detect that the value has not been set and correct it.

@rezxv
Copy link

rezxv commented Aug 24, 2016

Is the first option actually supported? I've tested it and the property's value resets to its type's default if the value is not provided in the yaml.

Edit: I've also searched the tests and unable to find one for this case.

@purpleidea
Copy link

@vincentbernat Great tips, I was wondering about both these approaches, and they work great! Small typo, I think you need an = sign:

  var stuff = Stuff{Max: 2147483647, Min: -147483648} # type is infered

Thanks again!

@purpleidea
Copy link

How do you do this for more complex structs where the sub structs should have a default... Eg:

type SomeStruct struct {
    A     string `yaml:"a"`
    Things struct {
        Foo []*Stuff `yaml:"foo"`
    } `yaml:"things"`
    Comment string `yaml:"comment"`
}

And if I want the elements in the Foo array to have defaults...

@purpleidea
Copy link

... It seems it looks like I need to implement the unmarshaler interface... https://godoc.org/gopkg.in/yaml.v2#Unmarshaler and do that per struct... Working on that...

@vincentbernat
Copy link

You need to implement UnmarshalYAML for Stuff:

func (s *Stuff) UnmarshalYAML(unmarshal func(interface{}) error) error {
    type rawStuff Stuff
    raw := rawStuff{} // Put your defaults here
    if err := unmarshal(&raw); err != nil {
        return err
    }

    *s = Stuff(raw)
    return nil
}

@purpleidea
Copy link

@vincentbernat Cool...

So it seems this:

type rawStuff Stuff

is basically an indirection to "cheat" and as a result, avoid the infinite recursion I was seeing when I didn't substitute in the temporary "raw" type... Is that analysis correct?

Is this the idiomatic pattern for this sort of thing?

Thanks again!

PS: This is now two projects that I'm using where I've seen your name pop up. I suppose I owe you a beverage at this point!

@vincentbernat
Copy link

Yes, your analysis is correct. I can't say about the idiomatic part as I am just a regular user (and not a very experienced one yet). I'll take the beverage at any point, but I am likely to forget in two weeks. :)

@niemeyer
Copy link
Contributor

Nice exchange. Closing as it seems the questions have been sorted.

@purpleidea
Copy link

@niemeyer I'd say this needs a doc patch before you close. Can you reopen and change the title please?

@niemeyer
Copy link
Contributor

If you have a clear idea of what to document, please go ahead and submit a proposal.

encodeering added a commit to encodeering/codesk that referenced this issue Feb 19, 2019
* the type has to be duplicated as it would lead to an infinite recursion (stack overflow) otherwise

NOTE

* go-yaml/yaml#165
@anthonyraymond
Copy link

You need to implement UnmarshalYAML for Stuff:

func (s *Stuff) UnmarshalYAML(unmarshal func(interface{}) error) error {
    type rawStuff Stuff
    raw := rawStuff{} // Put your defaults here
    if err := unmarshal(&raw); err != nil {
        return err
    }

    *s = Stuff(raw)
    return nil
}

Nice and smart trick, but i won't work if you pass an empty string to the unmarshaller:


func main() {
    unmarshalled := &speedProviderConfig{}
    yamlStr := ""
    err := yaml.Unmarshal([]byte(yamlStr), unmarshalled)
}

type speedProviderConfig struct {
	MinimumBytesPerSeconds int64 `yaml:"min"`
	MaximumBytesPerSeconds int64 `yaml:"max"`
}

func (c *speedProviderConfig) UnmarshalYAML(value *yaml.Node) error {
	type rawSpeedProviderConfig speedProviderConfig // create a subtype to trick goyaml. If we where using Decode() on a speedProviderConfig it will go into an infinite recursion
	conf := rawSpeedProviderConfig{
		MinimumBytesPerSeconds: 5000,
		MaximumBytesPerSeconds: 15000,
	}

	err := value.Decode(&conf)
	if err != nil {
		return errors.Wrapf(err, "bandwidth.speedProviderConfig: failed to unmarshall")
	}

	*c = speedProviderConfig(conf)

	return nil
}

The above main() function will produce a speedProvider value 0 and 0. If the strign is empty is seems that the umnarshalling is not taking place at all, and it also means that our default values wont be set.

What i recommend is :

func main() {
    unmarshalled := defaultSpeedProviderConfig()
    yamlStr := ""
    err := yaml.Unmarshal([]byte(yamlStr), unmarshalled)
}

type speedProviderConfig struct {
	MinimumBytesPerSeconds int64 `yaml:"min"`
	MaximumBytesPerSeconds int64 `yaml:"max"`
}

func defaultSpeedProviderConfig() *speedProviderConfig {
	return &speedProviderConfig{
		MinimumBytesPerSeconds: 5000,
		MaximumBytesPerSeconds: 15000,
	}
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

6 participants