Skip to content

This issue was moved to a discussion.

You can continue the conversation there. Go to discussion →

[SUGGESTION] Null(ptr/opt) coalescing for safer pointer dereferencing and more pleasant optional usage #320

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

Closed
torshepherd opened this issue Apr 4, 2023 · 6 comments

Comments

@torshepherd
Copy link

torshepherd commented Apr 4, 2023

The canonical case

https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#i12-declare-a-pointer-that-must-not-be-null-as-not_null

Smart pointers can be nullptr. Dereferencing them is therefore always dangerous, and the following is the main way of guaranteeing safety:

struct P { int innerVal; };
std::shared_ptr<P> x;
std::optional<int> inner = x ? std::optional(x->innerVal) : std::optional<int>();

This is a pain that stems from two issues:

  1. std::shared_ptr does not have first-class optional semantics
  2. C++ does not have sugared optional operators for null coalescing

Optional sugar

Modern languages like Typescript and Rust provide ways to really nicely work with optional types using different forms of null coalescing to values.

Typescript:

let x: string | undefined;
y = x! // throw if undefined. y must have type string
y = x ?? 'hello' // shorthand for x ? x : 'hello'. y must have type string
y = x?.endsWith('ello') // If x is undefined, return undefined. Otherwise do the thing. y must have type bool | undefined

The last one, in particular, allows for method chaining with early-exit if the return is undefined at any point along the chain.

This sugared way of working with optional types means that there is basically no reason not to do so whenever it makes sense because the barrier to usage is so low. The TSC tooling in particular makes it a breeze to use these things in a modern text editor, doing stuff like converting . to ?. if the left hand side of the dot access is an optional type. It's awesome.

The proposal

Two ideas (can be decoupled).

  1. Sugary operators for null coalescing. Could just use the typescript ones directly to apply to std optional IMO.
x: std::optional<std::string>;
y = x!; // x.value()
// Alternatively could be:
y = x as std::string;
y = x ?? "hello"; // x.value_or("hello")
y = x?->ends_with("ello"); // x ? std::optional<bool>(x->ends_with("ello")) : std::optional<bool>();
  1. First-class optional support for smart pointers. Consider the following code:
struct P { int innerVal; };
x: std::shared_ptr<P>;
inner = x?->innerVal;

IMO this would make working with both boxed values and optional types a real pleasure compared to where we are now in C++.

Other options

  • Some way to encourage people to use not_null smart pointers instead of regular nullable smart pointers. Maybe offer not_null_shared_ptr and optional_shared_ptr = std::optional<std::shared_ptr>

Tooling support

One could easily imagine a clang-tidy-like rule that checks for unchecked access to smart pointers and suggests changing -> to ?->. This would make finding and preventing potential segfaults an automated process, with the only manual effort going into handling the nullopt cases of the optional dereferencing operator.

Let me know if this sounds intriguing and I can work on a PR!

@msadeqhe
Copy link

msadeqhe commented Apr 5, 2023

I think as can be related to this topic as described in this PR.

@gregmarr
Copy link
Contributor

gregmarr commented Apr 5, 2023

I think we had a discussion where Herb talked about how he talked with the C# team and that there were issues with having the nullable support throughout the language, and that he didn't want to tackle that just yet. I tried searching for it but couldn't find it.

@JohelEGP
Copy link
Contributor

JohelEGP commented Apr 5, 2023

That's #27 (comment).

@gregmarr
Copy link
Contributor

gregmarr commented Apr 5, 2023

Thanks @JohelEGP!

@gregmarr
Copy link
Contributor

gregmarr commented Apr 5, 2023

I'm especially wary of the C# treatment of optional and null, because for years now I've been hearing from the C# design team that plumbing nullability through the C# language cost more than it was worth. We should learn from the experience of other languages, not only what they did but how it worked out for them and whether they would do it again. (I don't know if that experience about language support for nullability was the same as their language support for optionality.)

I can see "plumbing nullability through the C# language" as being very different than "syntactic sugar for dealing with null/optional". However, it does have some pitfalls that need to be dealt with as far as short circuiting and single execution that could be difficult to deal with in the conversion to C++1. I could also see wanting to combine nullability with the general concept of using as to determine "emptyness".

@msadeqhe
Copy link

msadeqhe commented Apr 6, 2023

This issue comment is related to this topic.

So it seems C++2 will use is and as instead of ??, ?. and nullable types...

Repository owner locked and limited conversation to collaborators Aug 30, 2023
@hsutter hsutter converted this issue into discussion #646 Aug 30, 2023

This issue was moved to a discussion.

You can continue the conversation there. Go to discussion →

Projects
None yet
Development

No branches or pull requests

4 participants