Swift Pointer Syntatic Sugar

While I think sugar for unsafe pointers is unlikely to get much traction in the short term, I do agree that it is an interesting topic for discussion. C is arguably a domain-specific language for twiddling pointers unsafely; almost every other language fails to match the concision of C in this regard.

I do think that a broader view is needed here however. A shorthand for UnsafeMutablePointer might be nice for some cases, but what about UnsafePointer and UnsafeBufferPointer? It is not clear that privileging one pointer type over the others is the right approach. Also, seeing the word "Unsafe" appear in code is a big red flag that the reader should proceed carefully; a single * obscures the fact that unsafe things are happening and arguably makes it too "easy" to do the wrong thing.

There are also various operations on pointers that might benefit from sugar. We should also think about how this interacts with any future borrow checking or ownership features as well.

6 Likes

shorthand for UnsafeMutablePointer might be nice for some cases, but what about UnsafePointer and UnsafeBufferPointer?

I never use UnsafePointer or the buffer variant. Why would you even use the buffer variants?

a single * obscures the fact that unsafe things are happening and arguably makes it too "easy" to do the wrong thing.

Maybe provide a dismissible compiler warning?

In addition to Slava's points, I would also add that the vast majority of Swift code doesn't use unsafe pointers. That code can currently use prefix * as a custom operator for whatever purpose their codebase would benefit from.

If your particular codebase uses pointers very heavily, you can always abbreviate the pointer type's name with typealias Ptr<T> = UnsafeMutablePointer<T>; then your code can use Ptr<Bytecode>. You could even abbreviate it to P<Bytecode> if Ptr is too long.

5 Likes

We considered having type sugar for the unsafe pointer types back before Swift 1.0 and intentionally decided against it. I'm not saying that we can never reconsider that decision, but I personally still feel we made the right decision.

12 Likes

If your particular codebase uses pointers very heavily, you can always abbreviate the pointer type's name with typealias Ptr<T> = UnsafeMutablePointer<T> ; then your code can use Ptr<Bytecode> . You could even abbreviate it to P<Bytecode> if Ptr is too long.

But thank of a developer. They want to split a c string by new lines.

They see Ptr<Ptr<CChar>>. This is not a Swifty way of doing things

I guess this is closed now

The buffer variants manage their storage; you can allocate space for one or more elements and free it. The raw variants don't have an element type; just a blob of bytes. Both are useful in addition to the basic UnsafePointer and UnsafeMutablePointer variants.

One approach is to add a new "unsafe" block or function annotation and only allow the sugar to be used within that scope. These are all fine ideas to discuss, but it merits more design than just adding a simple sugar for an existing type constructor.

I would suggest the developer convert the C string to a Swift String and split it with standard library or Foundation APIs. This way you get memory safety and correct behavior with non-ASCII characters.

I would really not want to do anything to make it easier to call libc string APIs from Swift. There are many legitimate uses for unsafe pointers; this isn't it :)

9 Likes

How to close topic?

I can close it for you if you really want. We don't usually close threads, though.

Oh, Ok

Right. I think we have the correct design here. It is important that you can express unsafe constructs in Swift, but equally important that it is clear in the code that this is happening. Unsafe being visible is a useful thing.

-Chris

12 Likes

Kinda out of the blue here, but would it be possible to make the shorthand type definitions extendable?
As in some way to define in code that T? := Optional<T>, [T] := Array<T> and [K: V] := Dictionary<K, V>.
If that were possible (under considerate grammar restrictions) then this whole topic can become just an utility that would be added ad-hoc for projects that do deal a lot with different wrapper types, as UnsafeMutablePointer<T> in this case.

2 Likes

I added a #withdrawn tag to the thread as a visual marker for readers. This also mimics how some previous proposals were withdrawn by their authors. It would be cool if the community would use this tag more often on ideas that are abandoned similar to how we mark topics with #off-topic that are unrelated to this forum.

1 Like

Ok

I like this idea. The problem with this approach is that people can abuse it. Also, the compiler must know what things have been made so that stdlib shorthand's are not redefined.

Instead of := maybe this:

shorthand T* = UnsafeMutablePointer<T>

But a problem arises when things like ? Is defined by this:

Int*?

Is this UnsafeMutablePointer<Int?> or UnsafeMutablePointer<Int>?

If we're reading left to right the obvious answer is UnsafeMutablePointer<Int>?. Compared to Int?* being UnsafeMutablePointer<Int?>.

But wouldn't the ? be more important than the * because it is a system-defined thing? Then we end up will shorthands having precedencegroups.

There's no way for "precedence" to cause the ? to bind before the * in that example; that would have to be its own complex grammar rule, like C's declarator syntax.

If we added user-customizable type sugar using the current operator parsing rules, that would actually be parsed as a single *? operator, which presumably wouldn't be declared and so would be ill-formed.

1 Like

Oh. Sorry

The important thing here is that if such s system were in place then the system ones would be defined through this as well and wouldn’t be compiler special exceptions