-
Notifications
You must be signed in to change notification settings - Fork 17.8k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
proposal: Go 2: add mandatory constructors for named type #28939
Comments
From what i understood I do not think it is a backwards compatible change for types that already have an init function defined before the new language change as code (e.g. var f frac) that does not have a matching constructor after the change to create an instance of the type will not compile anymore. Go is usually defined in a way to avoid hiding expensive operations behind simple constructs e.g. for Its also allows to break simple language constructs in the distance by adding code. By adding an init function with arguments all Note that go does not currently allow to define a method on the type with same name and different number of parameters so that would be another language change unless it would be a special exception for init only. There are other open questions for the proposal. If a value e.g. for map access for which a key is not present needs to be created or a value for a bare return would this also call a matching constructor or use the zero value or would it fail to compile as there may be no constructor with no arguments? In general I think we cant avoid misusing a type as the programmer can always use unsafe and other workarounds to not construct a type through the init function if they otherwise avoid the documented API contract for use of the type. The general Idea of constructors and destructors has been previously discussed in proposal #21737 which was rejected. |
Related to #21737. |
Go is designed to be verbose so that you know exactly what happens. Having |
I think it's great that you are trying to ensure the internal consistency of instances of your Frac struct. In Go we try to make default (zero initialised) instances be valid but this is not always possible. However there is a mechanism to do what you want already in Go, assuming Frac is in its own package, which it probably should be (even if just an internal package). You simply need to make the type unexported from the package. Then have the exported "constructor" return an instance of the type. For example:
Then from another package:
|
This seems like overkill for this specific use-case. What you're asking for has the same problems as operator overloading. Wouldn't it be easier to just allow the user to specify alternative defaults for a type? For example, type Frac struct {
n int
d int = 1
} Now the 'zero' value for I'm not really arguing in favor of this, for the record; things like this have been proposed and shot-down before, if I remember right. I'm just trying to provide a simpler alternative that doesn't involve hidden functions getting run without the user explicitly asking for it. |
When this has mattered for me, I end up doing something like: type T struct {
x, y int
userProperlyCalledConstructor bool
}
func NewT(x, y int) *T { return &T{x, y, true} }
func (t *T) M() {
if !t.userProperlyCalledConstructor {
panic("you didn't use the NewT constructor")
}
// ....
} I was also amused by @AndrewWPhillips's suggestion which is usually just a sign of bad API design. I never considered that pattern might have a valid use! |
We appreciate the problem that this issue is trying to solve. It might be nice to have better ways to ensure that a type can only be set to valid values. But this particular approach of requiring a constructor to run implicitly does not seem to fit the spirit of the language. It's also not clear how to handle composite literals with field keys, as in |
Has there been consideration of defined-type default initializers?
|
@networkimprov Default initializers as you describe them address a slightly different problem, in that they are restricted in the values they can set. I can't recall the idea having been proposed before. It's not clear to me that it's worth the cost. |
Problem
Say we are designing a fraction type:
According to the math definition, Frac.d must't be zero, so we need implement a New function in the package to serve as its constructor:
but the New function can't avoid misusing, such as
var f frac
or
f := frac{x, 0}
which are illegal fractions.
In short, the xxx.New() functions is just advisory constructors, but sometimes we need mandatory
constructors.
Proposal
This is a backward-compatible proposal to introduce new init functions to named types as its constructors. When initiate new instance of some type, if the type has no init function, the go compiler initiate it with its default ZERO value, but if the type has any explicit init function, the compiler must call its matched constructor to initiate the instance.
Syntax example:
Now user code:
var f frac
will not compile, because there has no matched constructor, and
f := frac{x, 0}
will call the
func (f *Frac)init(n, d int)
constructor.The text was updated successfully, but these errors were encountered: