How to disallow direct struct initialization - go

Given the following packages in Go, is it possible to prevent the direct initialization of Bar with Bar{..} without de-exposing Bar from the package?
package bar:
package bar
import ()
type Bar struct {
A string
B string
}
func NewBar(baz string) Bar{
return Bar{A:baz, B:baz+baz}
}
package main:
package main
import (
"fmt"
"./bar"
)
func main() {
x := bar.NewBar("sad") //all bars should be created with this
y := bar.Bar{A: "fadss"} //and this should be disallowed
bzzBar(x)
bzzBar(y)
}
func bzzBar(bzz bar.Bar) { //but I can't do 'Bar -> bar' because I want to use the type
fmt.Println(bzz)
}
My gut feeling says this can't be done, so that is also a valid answer.

There is no way to prevent Bar{} or Bar{A: "foo"}.
To control a struct the way you want you can return an interface instead and not export the struct itself.
Example given:
package bar
type Bar interface{
A() string
B() string
// if you need setters
SetA(string)
SetB(string)
}
type bar struct {
a string
b string
}
func (b *bar) A() string { return b.a }
func (b *bar) B() string { return b.b }
func (b *bar) SetA(val string) { b.a = val }
func (b *bar) SetB(val string) { b.b = val }
func NewBar(baz string) Bar {
return &bar{a:baz, b:baz+baz}
}

The idiom used in the Go standard library is:
package bar
package bar
import (
"fmt"
)
type Bar struct {
a string
b string
}
func New(baz string) *Bar {
return &Bar{a: baz, b: baz + baz}
}
func (b *Bar) BzzBar() {
fmt.Println(*b)
}
package main
package main
import (
"bar"
)
func main() {
x := bar.New("sad") //all bars should be created with this
x.BzzBar()
// error: unknown bar.Bar field 'A' in struct literal
// y := bar.Bar{A: "fadss"} //and this should be disallowed
}
Output:
{sad sadsad}
ADDENDUM:
The Go Programming Language Specification
The zero value
When memory is allocated to store a value, either through a
declaration or a call of make or new, and no explicit initialization
is provided, the memory is given a default initialization. Each
element of such a value is set to the zero value for its type: false
for booleans, 0 for integers, 0.0 for floats, "" for strings, and nil
for pointers, functions, interfaces, slices, channels, and maps. This
initialization is done recursively, so for instance each element of an
array of structs will have its fields zeroed if no value is specified.
Another idiom used in the Go standard library is to make zero values meaningful. For example, if new has not been explicitly initialized it will have the zero value default of false.
type Bar struct {
new bool
a string
b string
}
For example,
package bar
import (
"fmt"
)
type Bar struct {
new bool
a string
b string
}
func New(baz string) *Bar {
return &Bar{new: true, a: baz, b: baz + baz}
}
func (b *Bar) notnew() {
if b == nil || !b.new {
panic("bar.Bar not bar.New")
}
}
func (b *Bar) Bzz() {
b.notnew()
fmt.Println(*b)
}
.
package main
import (
"bar"
)
func main() {
x := bar.New("sad") //all bars should be created with this
x.Bzz()
// error: unknown bar.Bar field 'A' in struct literal
// y := bar.Bar{A: "fadss"} //and this should be disallowed
// var b bar.Bar
// panic: bar.Bar not bar.New
// b.Bzz()
// var b = bar.Bar{}
// panic: bar.Bar not bar.New
// b.Bzz()
// var bp *bar.Bar
// panic: bar.Bar not bar.New
// bp.Bzz()
// var bp = new(bar.Bar)
// panic: bar.Bar not bar.New
// bp.Bzz()
}
Output:
{true sad sadsad}

You can make all Bar fields unexported and provide getters and setters for them. This way package users will still be able to do silly things like
a := Bar{}
b := Bar{"foo"}
neither or which seems useful (although the former can be used to create an empty Bar similar to &bytes.Buffer{}).

You should be able to not export A and B, if you provide a String() function:
type Bar struct {
a string
b string
}
func NewBar(baz string) Bar{
return Bar{a:baz, b:baz+baz}
}
func (Bar) String() string {
return a + " " b
}

Related

Type conversion . Unable to convert to string in golang [duplicate]

In this bizarre example, someone has created a new type which is really just a string:
type CustomType string
const (
Foobar CustomType = "somestring"
)
func SomeFunction() string {
return Foobar
}
However, this code fails to compile:
cannot use Foobar (type CustomType) as type string in return argument
How would you fix SomeFunction so that it is able to return the string value of Foobar ("somestring") ?
Convert the value to a string:
func SomeFunction() string {
return string(Foobar)
}
Better to define a String function for the Customtype - it can make your life easier over time - you have better control over things as and if the structure evolves. If you really need SomeFunction then let it return Foobar.String()
package main
import (
"fmt"
)
type CustomType string
const (
Foobar CustomType = "somestring"
)
func main() {
fmt.Println("Hello, playground", Foobar)
fmt.Printf("%s", Foobar)
fmt.Println("\n\n")
fmt.Println(SomeFunction())
}
func (c CustomType) String() string {
fmt.Println("Executing String() for CustomType!")
return string(c)
}
func SomeFunction() string {
return Foobar.String()
}
https://play.golang.org/p/jMKMcQjQj3
For every type T, there is a corresponding conversion operation T(x)
that converts the value x to type T. A conversion from one type to
another is allowed if both have the same underlying type, or if both
are unnamed pointer types that point to variables of the same
underlying type; these conversions change the type but not the
representation of the value. If x is assignable to T, a conversion
is permitted but is usually redundant. - Taken from The Go
Programming Language - by Alan A. A. Donovan
As per your example here are some of the different examples which will return the value.
package main
import "fmt"
type CustomType string
const (
Foobar CustomType = "somestring"
)
func SomeFunction() CustomType {
return Foobar
}
func SomeOtherFunction() string {
return string(Foobar)
}
func SomeOtherFunction2() CustomType {
return CustomType("somestring") // Here value is a static string.
}
func main() {
fmt.Println(SomeFunction())
fmt.Println(SomeOtherFunction())
fmt.Println(SomeOtherFunction2())
}
It will output:
somestring
somestring
somestring
The Go Playground link
You can convert like this:
var i int = 42
var f float64 = float64(i)
check here
you can return like this:
return string(Foobar)

In Go can I use generics to declare the same method to different structs?

Is there a way to use golang 1.18 generics with struct methods? Ie something like this such that I have a common method SayName for both Foo and Bar:
package main
import (
"fmt"
)
type Foo struct {
Name string
Number int
FooID string
}
type Bar struct {
Name string
Number int
BarID string
}
func [T Foo|Bar](x *T) SayName() {
fmt.Printf("%d \"%s\"", x.Number, x.Name)
}
func main() {
foo := Foo{Name: "Name 1", Number: 1, FooID: "Foo123"}
foo.SayName()
bar := Bar{Name: "Name 2", Number: 2, BarID: "Bar456"}
bar.SayName()
}
I know this can be done in other ways using a base type and struct embedding or interfaces on each, but this is just a contrived example to keep things simple.
UPDATE: To make this more clear, what if I have a slightly less contrived example like below. I know about struct embedding and using base interfaces. But in the case below if SayName was defined as func (b *Base) SayName() {... I would get a run time error because the Base type does not have the GetID interface (it would be nil). So I want to pass in a generic which will have this interface on the Foo and Bar instances. Am I missing something?
For instance this code below would all work if I duplicate the SayName function for each type (as seen in the commented out section)
package main
import (
"fmt"
)
type Base struct {
Name string
Number int
}
type GetIDIface interface {
GetID() string
}
type Foo struct {
Base
GetIDIface
FooID string
}
func (f *Foo) GetID() string {
return f.FooID
}
type Bar struct {
Base
GetIDIface
BarID string
}
func (b *Bar) GetID() string {
return b.BarID
}
func [T Foo|Bar](x *T) SayName() {
fmt.Printf("Number %d \"%s\" with ID of %s", x.Number, x.Name, x.GetID())
}
/* THIS WORKS
func (x *Foo) SayName() {
fmt.Printf("Number %d \"%s\" with ID of %s\n", x.Number, x.Name, x.GetID())
}
func (x *Bar) SayName() {
fmt.Printf("Number %d \"%s\" with ID of %s\n", x.Number, x.Name, x.GetID())
}*/
func main() {
foo := Foo{Base: Base{Name: "Name 1", Number: 1}, FooID: "Foo123"}
foo.SayName()
bar := Bar{Base: Base{Name: "Name 2", Number: 2}, BarID: "Bar456"}
bar.SayName()
}
The boring and overall better solution is to declare those methods on each struct that needs them. This also makes the structs implement a hypothetical interface with SayName() method, should you need it. Beyond that, there is no language construct to declare the same method on different receivers.
What you could (but not necessarily should) do, is using a generic struct with a type constraint that restricts to Foo and Bar, however you can't access common fields of those structs. So the constraint couldn't even be Foo | Bar as we'd all like; instead you'd need methods to access those fields.
In your case you already have the GetIDIface, which Foo and Bar implement, so you can embed that:
type FooBar interface {
Foo | Bar
GetIDIface
}
type Base[T FooBar] struct {
Item T
Name string
Number int
}
func (x *Base[T]) SayName() {
fmt.Printf("Number %d \"%s\" with ID of %s\n", x.Number, x.Name, x.Item.GetID())
}
You can see the complete example in the playground.
However this reverses the relationship between Base and Foo/Bar. I.e. Base is not really a "base" anymore; it's a wrapper. Make sure it fits the semantics of your application, if you want to go down this route.
An alternative is to use a top level function that takes an interface with the necessary functionality. This way generics aren't needed:
type Base struct {
Name string
Number int
}
// Add getter on the base type itself
func (b Base) GetBase() Base {
return b
}
type Foo struct {
Base // embedding Base promotes GetBase() too
FooID string
}
// other Foo methods
type Bar struct {
Base // embedding Base promotes GetBase() too
BarID string
}
// other Bar methods
// interface with required functionality
type Sayer interface {
GetBase() Base
GetIDIface
}
func SayName(x Sayer) {
fmt.Printf("Number %d \"%s\" with ID of %s\n", x.GetBase().Number, x.GetBase().Name, x.GetID())
}
Playground: https://go.dev/play/p/4RBxW9D53Q8

How can I prevent initialization of an exported type in Go?

Edit: I'm not asking how to initialize variables correctly. I'm asking how to prevent them from being initialized incorrectly, so that functions that take that type don't have to explicitly validate their arguments.
In Go, I have a type that contains maps, e.g.:
package foo
type Bar struct {
baz map[string]int
total int
}
func NewBar() *Bar {
return &Bar{baz: map[string]int{}}
}
func (b *Bar) IncrementBaz(key string) {
f.baz[key] = f.baz[key] + 1
f.total++
}
// ...
If another package initializes the type and then tries to write to it, it will try to write to a nil map, causing a segfault:
package main
import "foo"
func main() {
var b foo.Bar
b.IncrementBaz("some_key") // write to nil map -> segfault
// ...
}
To prevent this, I want to make it so that other packages can't initialize an empty foo.Bar.
I can't simply make it unexported, because I want functions in other packages to be able to declare it as an argument or return type. If I wrap it in an interface, I get a similar problem:
package "foo"
type Bar interface {
IncrementBaz(string)
// ...
}
type bar struct {
baz map[string]int
total int
}
func NewBar() Bar {
return &bar{baz: map[string]int{}}
}
func (b *bar) IncrementBaz(key string) {
f.baz[key] = f.baz[key] + 1
f.total++
}
// ...
Similar problem:
package main
import "foo"
func main() {
var b foo.Bar
b.IncrementBaz("some_key") // method call on nil interface -> segfault
// ...
}
Is there any way to have an exported type that only the package that declares it can initialize it?
Now foo.Bar is just an interface type. The default value is a nil pointer. You are very close, just initialize it as b := foo.NewBar()

Why fmt.Println a struct does not use String() method of it's members

package main
import (
"fmt"
)
type bar struct {
}
func (b bar) String() string {
return "bar"
}
type foo struct {
b []*bar
bb *bar
}
func main() {
f := foo{b: []*bar{&bar{}}, bb:&bar{}}
fmt.Println(f, f.b, f.bb)
}
Why the result is
{[0x176f44] 0x176f44} [bar] bar
Not
{[bar] bar} [bar] bar
Are there any reasons behind it? It seems easy to implement and good for readability.
You have several problems in your code. You define Stirng on bar which is unexported, your fields are unexported as well. This works:
type Bar struct {
}
func (b Bar) String() string {
return "bar"
}
type foo struct {
B []Bar
BB Bar
}
func main() {
f := foo{B: []Bar{Bar{}}, BB: Bar{}}
fmt.Println(f)
}
Playground: https://play.golang.org/p/OhoIcB7cA3.
This would also work with *Bar.

How can I make two objects comparable

I'm passing objects of two different structs to a function where it's compared with an existing object saved as interface {} type.
In the following how can I make two objects comparable for Equality ===
In this attempt, comparison with bar works fine but with foo it throws a panic error in spite both objects are of struct type
Go Playground
package main
import "fmt"
type Foo struct {
TestMethod func(str string)
}
type Bar struct {}
type IQux interface {
Compare(object interface{}) bool
}
type Qux struct {
Method func(str string)
Reference interface{}
}
func (qux Qux) Compare(object interface{}) bool {
return object == qux.Reference
}
func testMethod(str string) {
fmt.Println(str)
}
func main() {
foo := Foo{TestMethod:testMethod}
bar := Bar{}
ob := &Qux{Method: foo.TestMethod, Reference: foo}
ob.Compare(bar) // works fine
ob.Compare(foo) // panic: runtime error: comparing uncomparable type main.Foo
}
You have a little typo, just try:
package main
import "fmt"
type Foo struct {
TestMethod func(str string)
}
type Bar struct {}
type IQux interface {
Compare(object interface{}) bool
}
type Qux struct {
Method func(str string)
Reference interface{}
}
func (qux Qux) Compare(object interface{}) bool {
return object == qux.Reference
}
func testMethod(str string) {
fmt.Println(str)
}
func main() {
foo := &Foo{TestMethod:testMethod}
bar := Bar{}
ob := Qux{Method: foo.TestMethod, Reference: foo}
ob.Compare(bar) // works fine
ob.Compare(foo) // panic: runtime error: comparing uncomparable type main.Foo
}

Resources