Getters, Setters & Structs

Hello everyone!

Lately I’ve been porting one of my classes projects to Go from Java. And so far what has been the hardest thing to understand since it seems it isn’t “standard” is:
When should I use getters and setters, and when should I use exported vs unexported fields in structs?

The current project I’m working on is an implementation of the MPEG-DASH algorithm and I work with Movies, Tracks and Segments.
A Movie has x Tracks.
A Track has y Segments.
The Segment then has the code in a byte array/slice.

All struct fields are unexported, they are exposed via getters and the struct build with a “pseudo” constructor - CreateXXX

However the segment doesn’t have the byte array from the get-go and so I ended up adding a SetContent

My question is: Should the struct fields be exported or not?
My logic is: since the only one that is set after the struct is created is the byte array, all fields should be unexported so they cannot be changed, being then exposed by a getter

1 Like

It’s unclear without some code or more data, really. But, getters and setters as such are very unusual in Go code. Given that we’re talking about exportedness I guess we’re at an API boundary of some kind. So what is the use case for the struct? If it’s something like a return value,

func CreateMovieSegment(...) Segment { ... }

type Segment struct {
  Name string
  Track int
  Segment int
  Content []byte
}

seems perfectly fine to me. Yes, I can change the fields after the fact, but that doesn’t matter. A function that returns an int suffers from the same problem. If the returned struct has a bunch of behavior and the actual Track, Segment etc are not things the consumer should concern themselves with you can instead return an interface.

type MovieThing interface {
   EncodeSegment() ([]byte, error)
}

Maybe EncodeSegment uses the Content and Track information to create an encoded version, or something. Returning this hides the fields and the implementation.

But maybe you have some other reason for wanting getters and setters?

So, erh, I guess, show some code so we know what we’re talking about? :slight_smile:

2 Likes

Hey Jakob! I’ll create a repo on GitHub ASAP
But meanwhile you can also answer to one of my questions about the fields, when should they be “public” or “private”?

It depends. :slight_smile: If you need to expose the field it should be exported - a getter plus a setter won’t gain you anything. If there is some other invariant or behavior needed (fields must be protected by a mutex, must maintain some relationship between them, etc.) then you need methods for that of course. So it depends on what the struct represents and what you want to use it for.

Some examples for inspiration.

  • io.LimitedReader, which exports its two fields for convenient inspection and reuse.
  • io.PipeReader, which exports nothing and just provides behavior, and hence also no getters or setters
  • image.Color variants which are just data containers and so export their fields, while having other functionality on top for conversion etc

and so on. Getters and setters are unusual. Looking through the standard library to see what it does in situations similar to your own is a good way to get a feeling for what’s usual.

1 Like

But if I only want it to be seen and not modified doesn’t the setter help?

Repo now online: https://github.com/jmg-duarte/go-dash

I’ll write a README about the “architecture” soon
But for short, I have a website requesting the segments to the “client” and this client should be getting the segments from a Docker container running a “server”

Keep in mind that is adapted from a college project, I know the architecture doesn’t make “sense” being that the client is sending the segments it wants to watch

(I assume you mean getter, as the setter would allow it to be modified.) But why care, really? It looks like your struct in this case is mostly just a plain data object;

// Segment represents a media file with a name and the segment in bytes
type Segment struct {
	filename string
	filesize int
	segment  []byte
}

You’re giving this to someone to give them information about a segment. What they do with it after that point sort of isn’t your business, as the data producer, outside of some sort of “data bondage” like desire to enforce your will. :slight_smile: In Go, you would typically just export the fields. Clearly, if you need to enforce some sort of consistent state in the object - like, the filename can be modified but then the filesize must be updated accordingly - then you want a setter that handles this and keep the fields unexported.

The other side of this coin is there are many types of segments and you want your code to be generic. Then you create getters and return an interface - a typical example might be the os.FileInfo which is just an interface that defines a bunch of getters. The reason is that the actual underlying type will be some OS specific thing, and it just has to define the specified getters to work.

2 Likes

Hello, Sir!

Just recently took a quick look at your code at Github.
It’s maybe not what You exactly asked about, but You have in many cases not made use of the standard library,- that would have made your code much easier and smaller. The standard library is quite well documented. You may even run your own documentation server if you are not on Internet. F.ex by:
$ godoc -http=:6060

Regarding your question, Go-code live in packages, and variables, functions, structs, types and constants that start with an uppercase letter are exported, all others are not. That’s it. It’s up to You to design your packages so that they make sense. Expect lots of trial and errors in the beginning when learning a new language.

Regarding code-style, use an IDE which make use of “go fmt” and the program “golint” may help You if You are a beginner.

Good luck

I’m not using the standard for cases such as the HTTP requests in order to learn about how the sockets, and others work. Using the standard lib would help me but at the same limits me to knowing how to use it but not how it works!
I already knew about what is exported vs unexported and how to do it, however I was searching for the pattern in Go, when should I do this and that, etc
I’m using VSCode with the Go plugin and JetBrains Goland

Hello again, Sir!

Ok, I understand, then, please, excuse me for not knowing.

Personally I am not that concerned about the absolutely right pattern. Go is for me so small and strict that in many cases the language gives itself what to do and not to do. If I wonder about anything, I often read code that others have written and try to learn from them. I find source-code for Go-packages on Github very useful in that sense.

Good luck

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.