Removing enumerated?

This is exactly the situation where you should use indices instead, which does the right thing for any collection.

2 Likes

Should I understand that a proposal would be to drop enumerated() ?

Why would you do that. enumerated has not the right semantic for what you are trying to achieve, but it does not make it useless.

Just spitballing here. Why don't we just change it to have the signature:

func enumerated(from: Int = 0) -> EnumeratedSequence<Self>

Then you can enumerate indicies using:

mySeq.enumerated(from: mySeq.startIndex)

The from version popping up in autocomplete may cause people to consider where it starts. Seems like an easy change that doesn't break anything or add maintenance burden...

Edit: I would also add that for UI or user facing purposes, it is nice to have the ability to start from 1...

1 Like

startIndex isn't always an Int, and this wouldn't really be recommended practice for ArraySlice.

I do think enumerated is a little redundant with zip now that we have one-sided ranges: zip(1..., mySeq). But it's not so bad that we need to take it out.

2 Likes

Could you explain more about why it wouldn't work with ArraySlice?

My question was about : how to avoid an unsafe use of enumerated that causes a crash at runtime.

Yes, if used correctly (which was not the case in my first example), there is no problem with it. But I feel, this is not fully meeting the design goal of Swift to be a safe language.

Ideas similar to Jon_Hull's could be a way ?

The important point is that it wouldn't work for, say, String.

It would work for ArraySlice, but it wouldn't be the recommended way to work with ArraySlice because it's perpetuating a misunderstanding of Swift's collection model: the subscript operation on a collection does not take an offset; it takes an index. Offsets and indexes are the same for Array and pretty much no other collections, including ArraySlice (and Foundation.Data).

3 Likes

"Safe" doesn't mean "never traps;" it means "never accesses uninitialized memory, or accesses memory as some type other than what was initialized there." Trapping is Swift's way of avoiding such unsafe accesses.

9 Likes

The fact that this misunderstanding keeps cropping up, year after year, time and time and time again, tells me that to a large number of people out there, “safe” in the context of a programming language *does* mean “never traps”.

The property you are describing is properly termed “memory safe”. It has become commonplace in Swift parlance to abbreviate “memory safe” as “safe”, but evidence indicates that this abbreviation is not readily understood, nor in widespread use.

I think the most prudent course of action is to stop using that abbreviation, because it does not convey the desired information in practice. Either Swift should come up with a different abbreviation, or just say “memory safe” without abbreviating it.

After all, clarity at the point of use is more important than brevity.

• • •

As an illustrative example, suppose someone were to write the autopilot program for a self-driving car in Swift. If that program traps at runtime, well golly gosh, it certainly did avoid accessing the wrong piece of memory. But, by trapping at runtime, the real-world outcome could well be an automobile collision that causes actual human injury or death.

That is definitely not “safe” by any reasonable definition of the term.

Therefore, using “safe” to refer exclusively to “memory safe” is not reasonable.

4 Likes

You can make this distinction, but if the program does not trap at run time and instead, say, returns nil from findPedestrianInMyPath, then you still get an automobile collision that causes actual human injury or death.

Using "safe" to refer to "the program doesn't crash" is a bad idea, and I think we should continue correcting that misapprehension whether or not we decide to stop using the term "safe" ourselves.

10 Likes

enumerated() is confusing in its current form. If we are ever going to change anything about it, I'd rather we go the whole hog and properly remove it, rather than patch on default arguments or whatever to try to make it semantically relevant.

The reality is Swift's source-breaking policy means that unless enumerated() is actively harmful, it should just stay as is for the foreseeable future. I can't make strong arguments that it is harmful. It's 'fine'.

Undoubtedly. It doesn't change the fact that that isn't what we mean when we say “Swift is a safe language,” and @claude31 has misunderstood the design goal, however poorly it may be stated.

It is also true that we try to write our APIs to prevent confusion and misuse at compile-time, which could be a more casual interpretation of the word “safe.” But it is impossible to design reasonable APIs without preconditions in any reasonable everyday programming language (I'm lookin' at you, division by zero), so Swift is going to violate a casual definition of the word “safe” if that's what you're expecting.

Hello, MemoryUnsafeMutableBufferPointer!

7 Likes

: )

1 Like

Can you elaborate on the misuse? I think you are referencing the use in https://github.com/vapor/core/blob/fda3ebda8046af7c9a886fa4a5cfd886111ac23e/Sources/Async/Future%2BFlatten.swift#L58 and while it enumerates a collection, it uses the index to build up a new Array with the collections elements. That's perfectly fine isn't it?

This discussion is many years old. Any misuse would be fixed by now; either I or someone else filed a bug back in the day.

You have flipped the implication around here. The definition of safety includes, but is not limited to, memory safety. So it is perfectly reasonable to call an UnsafePointer an UnsafePointer and leave the user to infer the obvious reason why in particular it is unsafe.

That's different to objecting to someone describing some other cause of undue danger or risk as "unsafe" as a misuse of the term. This is not a more casual use; it's the dictionary definition. I agree with @Nevin that it isn't reasonable to claim that word exclusively for memory safety.

None of this is to say that enumerated, or trapping in general, can be described across the board as unsafe. Swallowing programming errors silently instead of trapping is also unsafe. IMO an API would be unsafe if a possible trap is not obvious and wouldn't easily be caught by some light testing. And I don't think that's the case here. enumerated might be confusing, and worth deprecating because of that, but it's not particularly unsafe.

An example of a trapping API that I think is reasonable to describe as "unsafe" would be if swapAt trapped when you tried to swap two elements at the same index. That's something that could easily be missed with moderate levels of testing, and there is a perfectly reasonable way to proceed when it is encountered (you do nothing). The old swap that took elements not indexes used to have to trap under these circumstances, but the new one doesn't have to so you can reasonably describe it as safer than the old API.

12 Likes

I do think a lot of people (unfamiliar with Swift Evolution) read "safe" as "Fault Tolerant" to a certain degree. If we want to use "safe" to mean memory safe, perhaps we should explicitly coin a new term to mean "won't trap" when dealing with Swift API, and then educate on the difference.

This keeps coming up. For example, a bunch of people want to add a non-trapping subscript to arrays which return nil when out of bounds, but they have all labeled it as "safe:" in their own code... and that won't fly in the standard library because it implies that the standard subscript is "unsafe". Everyone is stumped on a good name because anything that is a synonym of safe has the same issue.

We should officially anoint a (short) term to mean this other type of safety...

1 Like

I think the large point being made above is that, whatever this behavior may be, it is not a “type of safety.”

The community did settle on a term for this several years ago, and it was “lenient.”

Fault tolerance is absolutely a type of safety!

1 Like