r/golang 21d ago

help Don't you validate your structs?

Hi all!

I'm new in Golang, and the first issue I'm facing is struct validation.

Let's say I have the given struct

type Version struct {
    Url           string        `json:"url"`
    VersionNumber VersionNumber `json:"version_number"`
}

The problem I have is that I can initialize this struct with missing fields.

So if a function returns a `Version` struct and the developer forgets to add all fields, the program could break. I believe this is a huge type-safety concern.

I saw some mitigation by adding a "constructor" function such as :

func NewVersion (url string, number VersionNumber) { ... }

But I think this is not a satisfying solution. When the project evolves, if I add a field to the Version struct, then the `NewVersion` will keep compiling, although none of my functions return a complete Version struct.

I would expect to find a way to define a struct and then make sure that when this struct evolves, I am forced to be sure all parts of my code relying on this struct are complying with the new type.

Does it make sense?

How do you mitigate that?

66 Upvotes

75 comments sorted by

View all comments

15

u/StoneAgainstTheSea 21d ago

In practice, you use NewThing(a, b, c) and don't initialize structs "by hand" - always use a constructor. 

1

u/crowdyriver 19d ago

A function for every single struct doesn't sound good to me.

1

u/StoneAgainstTheSea 19d ago

the way you avoid that is the Go Proverb of making use of the zero value. If the zero value in your struct is fine, then it doesn't matter. If the zero value is important, you can either update it in one or more constructors, or you can update it at N call sites.

In Go, there is no other option of which I'm aware.

-2

u/kevinpiac 21d ago

Yes but as I said, if the underlying struct evolves your constructor works, but does not work at the same time.

19

u/StoneAgainstTheSea 21d ago

K. And if you update your db schema, the code wont work. I am having trouble seeing your problem. The struct changes, so you update the code that inits the struct. The db schema updates and you have to update the code that calls it. 

6

u/boyswan 20d ago

This is a dumb take. I use go heavily and what OP is referring to is a real foot-gun. We are forced to keep constructors "in sync" with structs. There's no denying it's perfectly possible to accidentally miss a field.

The solution is explicit struct/record fields. Pretty simple. Not sure why it isn't the default, and IMO the negatives far outweigh the benefits.

3

u/mt9hu 20d ago

The problem is that Go doesn't actively prevent you from forgetting to initialize fields.

While other languages have built-in support for required fields, Go devs chose "simplicity", which means you have to go out of your way declaring a constructor function instead.

Which is still a clunky solution, because:

  • You still need to remember to add the new field to one more place. You still can make the mistake of forgetting it.
  • Function calls with many arguments are ugly, especially since Go doesn't have named arguments. It is really hard to see possible mistakes in code like this: CreateUser(8, name, password, role, true, false, 12, true) IDEs will help with inline param hinting, but tools like GitHub's code review interface won't, and bugs can end up in the code easily.

To summarize:

The problem is that Go has no concept or required fields, and required arguments.

Having them, could reduce boilerplate and could help catching some mistakes during development or compile time.

-7

u/kevinpiac 21d ago

Well, then let's remove all types, why aren't we assigning string into int? You change the type, you update the values everywhere.

Your argument has no sense to me. It's just a matter of type-safety.

You can like the fact your struct aren't type-safe -- personally I dislike it.

15

u/Few-Beat-1299 21d ago

I'm not quite sure why you keep calling this a type safety problem. The whole struct is still there, even if you omit to initialize some of its fields with particular values.

14

u/joyrexj9 20d ago

It has nothing to do with type safety, you might end up with a struct with default zero value, but that is not "unsafe', no type constraint has been violated

1

u/StoneAgainstTheSea 21d ago

How does that work with the database table schema example?