如何禁止直接结构初始化

huangapple go评论78阅读模式
英文:

How to disallow direct struct initialization

问题

在Go语言中,如果你不想将Bar从包中暴露出来,同时又想阻止使用Bar{...}直接初始化Bar,是否有可能实现呢?

我的直觉告诉我这是不可能的,所以这也是一个有效的答案。

英文:

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.

答案1

得分: 8

没有办法阻止Bar{}Bar{A: "foo"}的出现。

要按照您想要的方式控制一个结构体,您可以返回一个接口,而不是导出结构体本身。

给出一个示例:

package bar

type Bar interface {
    A() string
    B() string
    // 如果需要设置器
    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}
}
英文:

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}
}

答案2

得分: 4

你可以将所有的Bar字段设置为未导出,并为它们提供getter和setter方法。这样,包的用户仍然可以做一些愚蠢的事情,比如:

a := Bar{}
b := Bar{"foo"}

这两种情况都似乎没有什么用处(尽管前者可以用来创建一个类似于&bytes.Buffer{}的空的Bar)。

英文:

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{}).

答案3

得分: 4

Go标准库中使用的成语是:

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") //所有的bar都应该使用这个函数创建
	x.BzzBar()
	// 错误:在结构体字面值中未知的bar.Bar字段'A'
	// y := bar.Bar{A: "fadss"} //这样的写法应该被禁止
}

输出结果:

{sad sadsad}

补充说明:

Go编程语言规范

零值

当分配内存来存储一个值时,无论是通过声明还是通过调用make或new,如果没有提供显式的初始化,内存都会被赋予默认初始化。这个值的每个元素都被设置为其类型的零值:布尔类型为false,整数类型为0,浮点数类型为0.0,字符串类型为"",指针、函数、接口、切片、通道和映射类型为nil。这个初始化是递归进行的,因此,例如,如果没有指定值,结构体数组的每个元素的字段都将被置为零值。

Go标准库中使用的另一个成语是将零值赋予特定含义。例如,如果new没有被显式初始化,它将具有默认的零值false

type Bar struct {
	new bool
	a   string
	b   string
}

例如,

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") //所有的bar都应该使用这个函数创建
	x.Bzz()
	// 错误:在结构体字面值中未知的bar.Bar字段'A'
	// y := bar.Bar{A: "fadss"} //这样的写法应该被禁止

	// 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()
}

输出结果:

{true sad sadsad}
英文:

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}

答案4

得分: 1

如果你提供一个String()函数,你应该能够不导出AB

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
}

你可以在这里找到更多信息。

英文:

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
}

huangapple
  • 本文由 发表于 2014年10月20日 17:10:09
  • 转载请务必保留本文链接:https://go.coder-hub.com/26462043.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定