-
Notifications
You must be signed in to change notification settings - Fork 17.7k
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: explicit nil and pointer param #36884
Comments
How would you propose to solve the issue of breaking composability as presented above. This example shows you need to evaluate at compile time, leading to a twostage compilation thing. |
(p/s: null pointer detection is undecidable in the general case) |
For language change proposals, please fill out the template at https://go.googlesource.com/proposal/+/bd3ac287ccbebb2d12a386f1f1447876dd74b54d/go2-language-changes.md . When you are done, please reply to the issue with Thanks! |
you can't assign to a non-nil pointer, or dereference it until you have proven that it has been assigned a non-nil value? ie: Prove it with a branch...
Or prove it with a declaration that it's not nil. But if that pointer exists in a struct, must it be assigned on struct construction? |
This could get quite confusing type P struct { type q struct {} func (x *q) execute() func main() { is this permitted? if not, what check is required? what would happen if P and q were not in the same package as main, the expression if p.q == nil would not be allowed. |
@ianlancetaylor I'm not suggesting a language change that would require breaking the Go 1 compat from my understanding. I know that Go 2 is code word for it'll never happen. What i'm suggesting is this is specifically for function parameters only. Would you still like me to fill out the template? Again, i'm not an expert, and I do not know if we can do what i'm suggesting below is possible or not. @chewxy Great question. I'm sorry i forgot to put this in the original issue. func foo(a <- chan int) *Type1{
if <-a < 0 {
return nil
}
return newType1(a)
}
func example(a #Type1) {
...
}
go func(){
// read from network
ch <- fromNetwork()
}
// Compile error: cannot use untype nil as type *Type1 in argument to example
example(foo(ch))
v := foo(ch)
if v != nil {
// compiles successfully
example(v)
}
newVar := new(Type1)
// Compile error: cannot use newVar (untype nil) as type *Type1 in argument to example
example(newVar)
newVar = &Type1{}
// compiles successfully
example(newVar)
func call(t *Type1) {
if t == nil {
return
}
// compiles successfully
example(t)
} The goal would be to analyze the code to see if it is possible for the param to be nil. And if it is, at compile time, state this is invalid. I hope you don't mind me adding this to my original comment. 🙏 |
@atishpatel It's not correct to say that "Go 2 is code word for it'll never happen.'' We use the Go 2 label for every language change, including the language changes that have in fact happened. For example, see https://golang.org/doc/go1.13#language; each of those changes has an associated issue marked "Go 2". So, yes, I would still like you to fill out the template. Thanks. |
A language change that permits people to write if t == nil {
return
}
// Here t can be assigned to pointers required to be non-nil. requires very clear instructions for exactly when the compiler can assume that the pointer is not nil. There are multiple Go compilers, and they must all precisely agree as to which programs can be compiled. |
@ianlancetaylor My apologizes. I remember Rob Pike announcing Go1.10 by saying there probably won't be a Go2, and we are here Go1.14. I didn't realize the Go2 label is used for language changes that still keep the Go 1 compact. I'll fill out the template and add it to the original comment. Thank you for your help. 🙏 |
This is a language change. Also, not quite as useful given that you can write a program to check for trivial
This is confusing. |
Chewxy. Yes, this is a language change but i don't think it breaks go1 compat. Yes. The goal isn't to check if something is nil or not. The goal is write and share code that is more clear with pointers. I've written Go for 3+ years, and as my projects grow bigger and there is older code, i run into more issues with nil pointers. One could just deal with runtime errors to find out if there is a nil pointer issue. But, i'm trying to find a way where, if i know something shouldn't be nil, i can communicate that to everyone including my future self. chewxy. You are correct. I meant |
@gopherbot please remove label WaitingForInfo |
The problem with this idea is that not all, or not even most nuls can be checked at compile time. The compiler would have to solve the halting problem for this. Rather, if you don't want to receive a nul, see if you can't pass the struct by value in stead of by pointer. |
As mentioned above in #36884 (comment), in order to make code like this work if a != nil {
F(a) // where F is defined using #
} we need to add a notion of dataflow to the language, so that all compilers will analyze this code in the same way. That is a significant complexity that we want to avoid. The suggested For these reasons this is a likely decline. Leaving open for four weeks for final comments. |
No further comments. Closing. |
Summary
At compile time, there should be a way to specify you can't pass nil into the function call but it's still a pointer.
Pointers are awesome and there are many reason to use a pointer such as not having to copy a param, being able to mutate a param, etc. But, using pointer is makes you prone to nil pointers, and people who use your functions often try to pass nil and you have to handle this. I genuinely think there is potential here for a way to improve the developer experience by providing compile time errors for invalid nil pointers.
Side note: One of the main reasons I love Golang is because it's an opinionated statically typed language that provides a great developer experience. I get lint warnings if i don't add proper comments and that is wonderful.
Current options - landmine runtime error
Check all params for nil and panic or return error.
Goal
Proposal 1 - non-nil pointer character - backward compatible
Introduce a character that implies a non-nil-able pointer. In this example, the character is
#
.In this case,
param1
could be nil butparam2
would give a compile time error if someone passed in nil.I'm not an expert at the language so perhaps someone else can tell me if there is a better way than this.
Alternative Proposal 2 - Not Nil Union Type - backward compatible
This is more elegant but it could break people's code on library updates. For example, if you are relying on a library that updated to use this, your code would give compile time errors saying you can't use nil here. But, perhaps it is good because if the developer updates the library and you get compile time errors, you shouldn't have been passing nil into the function anyway and it saved you from a runtime error. 🤷♂
In this case,
param1
could be nil butparam2
would give a compile time error if someone passed in nil.Template
#
is a non nil pointer.#
would mean a pointer type that is not nilAt the core of this is a better developer experience by giving compile time error instead of runtime errors.
The text was updated successfully, but these errors were encountered: