Flagging lazy-loaded fields as being unsafe to access directly

107 views
Skip to first unread message

Traun Leyden

unread,
Nov 17, 2017, 4:00:27 PM11/17/17
to golang-nuts


I'm trying to figure out a way to best signal to other developers maintaining a single package that a field is unsafe to access directly.  In this case, it's being lazily loaded, and the only way to ensure it will be properly loaded is to access the method wrapper rather than directly accessing the field.

For example, see this code:

type foo struct {
   bar string
}

// Method wrapper that does lazy-loading
func (f *foo) getBar() string {
    if f.bar == "" {
        // lazy-load this from a database or some other expensive call
        f.bar = "bar"
    }
    return f.bar
}

func main() {
f := foo{}
fmt.Printf("f.bar directly: %v\n", f.bar)
fmt.Printf("f.bar lazy-loaded: %v\n", f.getBar())
}

If you access the field directly, you get an empty value.  If you call the method wrapper, you get the intended value.

I believe the official answer is just "do the right thing" since you are within the package boundaries and have full control.  And I'm pretty sure the language doesn't provide anything here.  

But what I'm wondering -- are there any common conventions or workarounds to avoid this pitfall?  So far the only idea I've been able to come up with is to use underscores and/or comments to clearly mark the field as being unsafe to access directly:

type foo struct {
   _bar string  // You probably want to use getBar() since this is lazily loaded an unsafe to access directly.
}


Nicholas Hanley

unread,
Nov 18, 2017, 1:27:05 AM11/18/17
to golang-nuts
In Go, lowercase identifiers are not visible outside the package in which they are defined (ref: Exported identifiers). Your example declares foo in the main package but I assume your real code would be in its own package. What you can do is export Foo and GetBar, but leave the bar field unexported.

Your code would look something like this:
main.go
package main

import (
"fmt"

)

func main() {
f := baz.Foo{}
fmt.Printf("f.bar lazy-loaded: %v\n", f.GetBar())
}

example.com/baz/foo.go
package baz

type Foo struct {
bar string
}

// Method wrapper that does lazy-loading
func (f *Foo) GetBar() string {
if f.bar == "" {
// lazy-load this from a database or some other expensive call
f.bar = "bar"
}
return f.bar
}

Traun Leyden

unread,
Nov 19, 2017, 9:08:47 PM11/19/17
to golang-nuts

In this case I mean within the boundaries of a single package.

I realize there is no way to enforce another package maintainer to call the method wrapper rather than the field directly, but I was wondering if people have come up with naming conventions such as a leading underscore to make it apparent that certain fields shouldn't be accessed directly under most circumstances.  

Nate Finch

unread,
Nov 19, 2017, 11:06:42 PM11/19/17
to golang-nuts
Just use a comment.  That's usually fine.  // this is lazy loaded, use getBar() instead of directly accessing.

"Don't do that" is a surprisingly feasible solution to a lot of programming problems.  Not everything has to be enforced by tooling.
Reply all
Reply to author
Forward
0 new messages