In go language, may I define allowed values of a string in a struct and/or force creation only via constructor? Or avoid direct creation of a struct?

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

In go language, may I define allowed values of a string in a struct and/or force creation only via constructor? Or avoid direct creation of a struct?

问题

我有一个名为Direction的结构体,其中有一个类型为字符串的value字段。Direction应该是NSWE之一。

type Direction struct {
    value string
}

受到这个问题的答案的启发:https://stackoverflow.com/questions/15323767/does-go-have-if-x-in-construct-similar-to-python,我猜测创建一个有效的Direction的好方法可能是这样的:

func NewDirection(direction string) Direction {
    switch direction {
        case "N", "S", "W", "E": return Direction{direction}
    }
    panic("invalid direction")
}

但这对我来说还不够,因为我仍然可以创建无效的Direction

d := Direction{"X"}

我还发现了这篇有趣的文章,关于在Go中强制使用构造函数。阅读这篇文章后,我发现需要使用另一个包。我可以在主包中创建一个"受保护"的结构体吗?

英文:

I have a struct Direction with a value of type string. Direction should be N, S, W or E.

type Direction struct {
    value string
}

Inspired by an answer of this question: https://stackoverflow.com/questions/15323767/does-go-have-if-x-in-construct-similar-to-python I guess a good way to create Direction in a valid manner can be this one:

func NewDirection(direction string) Direction {
    switch direction {
        case "N","S","W","E": return Direction{direction}
    }
    panic("invalid direction")
}

But this is not enough for me because I can still create invalid Direction:

d := Direction{"X"}

I also found this interesting article about enforcing the use of constructor in go. Reading this article I can see that is necessary the usage of another package. May I have a "protected" struct in, for example, main package?

答案1

得分: 3

你已经按照惯例完成了几乎所有应该做的事情:

  • 将字段设为未导出
  • 提供一个构造函数
  • 在类型上加上注释,说明应该使用构造函数,并解释如何处理零值(如果有必要)

现在,包的用户无法修改该字段,并且构造函数的存在清楚地表明应该调用它来创建有效的实例。这是标准库设定的惯例。

当然,还有其他方法可以使其更难以使值无效,但这本质上只是为了与想象中的对手进行一场无法赢得的武器竞赛而浪费时间和使代码过于复杂化。

如果有人不理解语言并且不阅读文档,那么他们总是会找到一种误用它的方法。如果他们正在积极地试图自毁,那么你无法阻止他们,也没有理由这样做。

在Go中,包是代码组织的最小功能单元。无法在文件级别上保护字段,例如。即使是同一个包中的文件,实际上也会像它们的所有代码都在同一个文件中一样运行。因此,与构造函数在同一个包中的任何代码都具有与构造函数相同的权限。

英文:

You've already done almost everything you should do by convention:

  • make the field unexported
  • provide a constructor
  • put a comment on the type stating that the constructor should be used, and explain how zero-values are treated (if that matters)

Now users of the package can't modify the field, and the existence of the constrictor makes it clear that it should be called to create valid instances. This is the convention that is set by the standard library.

Sure, there are other ways that you can make it even harder to invalidate values but this is essentially just wasting time and overcomplicating your code for the sake of an unwinnable arms race against an imaginary opponent.

If someone doesn't understand the language and doesn't read documentation, then they're always going to find a way to misuse it. If they are actively trying to sabotage themselves, there's nothing you can do to stop them and no reason to either.

Packages are the smallest functional unit of code organization in Go. There is no way to protect field at, for example, the file level. Even files within the same package effectively operate as if all their code was in the same file. Therefore, any code in the same package as the constructor will have the same privileges as the constructor.

huangapple
  • 本文由 发表于 2022年6月12日 06:26:56
  • 转载请务必保留本文链接:https://go.coder-hub.com/72588258.html
匿名

发表评论

匿名网友

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

确定