Go:禁止直接struct初始化

时间:2014-10-20 09:10:10

标签: go

考虑到Go中的以下软件包,是否可以阻止使用Bar直接初始化Bar{..}而不从软件包中解除Bar

bar

package bar

import ()

type Bar struct {
    A string
    B string
}

func NewBar(baz string) Bar{
    return Bar{A:baz, B:baz+baz}
}

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

我的直觉是说无法做到这一点,所以这也是一个有效的答案。

4 个答案:

答案 0 :(得分:4)

您可以将所有Bar字段取消导出,并为其提供getter和setter。这样,包用户仍然可以做一些愚蠢的事情,比如

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

既不是,也没有用(虽然前者可用于创建类似于Bar的空&bytes.Buffer{}。)

答案 1 :(得分:4)

无法阻止Bar{}Bar{A: "foo"}

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

给出的例子:

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)

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

输出:

{sad sadsad}

附录:

  

The Go Programming Language Specification

     

The zero value

     

当分配内存来存储值时,通过a   声明或调用make或new,并且没有明确的初始化   如果提供,则为存储器提供默认初始化。每   将此类值的元素设置为其类型的零值:false   对于布尔值,0表示整数,0.0表示浮点数,""对于字符串,没有   用于指针,函数,接口,切片,通道和映射。这个   初始化是递归完成的,所以例如每个元素   如果没有指定值,结构数组将使其字段归零。

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

输出:

{true sad sadsad}

答案 3 :(得分:1)

如果您提供A功能,则应该无法导出BString()

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
}