Hacker News new | comments | show | ask | jobs | submit login
Go Nil Pointers and Rust (getstream.io)
42 points by tschellenbach 5 months ago | hide | past | web | favorite | 13 comments



A minor nit: There is a major difference between Rust references and C++ references - while both are guaranteed to point at something, only a Rust reference is guaranteed to point at something valid. A C++ reference can point at an already freed object if you aren't careful which can be worse than a NULL pointer dereference since this can cause silent corruption.


Can you define better "guaranteed"? If you take a NULL pointer and dereference it you get a null reference. I realize the language standard insists this isn't possible, and so compiler developers have started feeling justified in saying "if (&myref == NULL)" is nonsense they can "optimize out" (as if I somehow typed that by mistake :/...); but that is just the compiler making an unwarranted and clearly incorrect "assumption": nothing in any of the language standard, the compiler, or the runtime "guarantees" that references point at something. If dereferencing a pointer was required to do a one byte read from the address that could not be elided by the compiler, or automatically injected the moral equivalent of "if (myptr == NULL) std::terminate();", that would be an entirely different story; but, as it stands, a dereference from a pointer to a reference is nothing more than a blind type assertion by the developer, and so there is no reason to believe a reference is somehow any less likely to be NULL than a pointer the developer always thought would point to something.


> compiler developers have started feeling justified in saying "if (&myref == NULL)" is nonsense they can "optimize out" (as if I somehow typed that by mistake :/...); but that is just the compiler making an unwarranted and clearly incorrect "assumption": nothing in any of the language standard, the compiler, or the runtime "guarantees" that references point at something

I don't see how there is nothing guaranteeing that your references are nonnull. My understanding was that the standard prevents you from having a null reference because the methods of trying to generate one are codified as illegal or performing undefined behavior. Am I wrong here, or misunderstanding what you're trying to say?


I'm not a C++ standard expert, but, here is my understanding: In C++, if you have a reference, it must refer to something. I'm not aware of any way to legally construct a reference that refers to NULL. Of course, we live in the real world, where, regardless of what the C++ standards say, anything is possible. A random bit-flip could cause the reference to refer to NULL. But, AFAIK, thats outside of the C++ standard.


Rust has algebraic data types. That’s completely different than the proposed solution for Go, which is pretty much what Kotlin does. And IMHO its a bandaid.

ADTs are the way to go. If Go had ADTs I would pick it over Rust in a heartbeat, as I personally don’t care much about not having a GC, and think go-routines are superior to async/await.


This sounds like a fit for OCaml with either Async or preferably Lwt. ADTs are natural in OCaml, and you can even have Generalised ADTs which can encode ADTs into the type system.

Granted, the main downside of OCaml is it still being single threaded. That can be worked around, but it depends on your workload.


Kotlin seems pretty nice. Been using it a lot lately. It's not perfect since you can still get into trouble but it forces you to opt in to that. You need to add a question mark to the type to indicate it is nullable and dereferencing without a null check is a compile error. You use two exclamation marks to bypass this to opt out of this. Of course if you are then wrong you may get npes.

But it works well enough that you can write code pretty easily that simply cannot produce npes. Excluding that as source of bugs is nice and should be built into any new language.


People complaining about nil haven't thought the problem through. It makes the language way more complicated if variables can't sit in an uninitialized state (with a default value of nil or zero instead of random bits). Basic stuff like named return values no longer work. Slices can only contain certain special zeroable types.

The right thing to do is to know statically in your code whether a variable can be nil, or can't. Don't put stupid little nil checks in every function, but maybe do so when storing a field, e.g. to ensure an object holds valid state.

If your biggest problem is surprise nil pointer values, then that's good for you, because they're relatively easy bugs to track down.


> Basic stuff like named return values no longer work.

If Rust hypothetically wanted to add named return values, I think the way they'd do it is have the compiler check that all codepaths through the function initialize the returns. In general rustc is happy to let you declare uninitialized variables, and even partially initialize them field-by-field, as long as it can prove to itself that everything's fully initialized by the time you read it.


This won't work the way Go does it, or else you end up with fiddly little rules about when you're allowed to take addresses of variables and such.


I looked for it in the article but I didn't see see it anywhere: In go you can have methods that execute with nil receivers. In practice this makes nil in go very different than null I'm java.

func main() { var a A a.getB().getM() }

type A struct { B B; }

func (a A) getB() B { log.Println("getB") if a != nil { return a.B; } return nil }

type B struct { M string; }

func (b *B) getM() string { log.Println("getM") if b != nil { return b.M; } return "" }

Everything is nil but they both got executed! Obviously, you should use this selectively, but it's there!

Algebraic Data Types always seemed more elegant when I've seen them in other languages, anyone have anything but positive things to say about them?


Isn't this just the Maybe monad without any benefits of abstraction?


I do like the idea of explicit non-nil pointers. While I have refactored code to explicitly exclude all uses of pointers unless absolutely necessary and (as mentioned in the article) studiously check for nil when receiving interfaces and pointers, it feels hacky as hell. Non-nil pointers would bring us halfway to a better place IMHO.




Guidelines | FAQ | Support | API | Security | Lists | Bookmarklet | Legal | Apply to YC | Contact

Search: