"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.
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.
You can make this distinction, but if the program does not trap at run time and instead, say, returns
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.
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.
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 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.
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...
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!
I would not characterize not trapping when an assertion fails as “fault tolerance.” There is no guarantee whatsoever that continued operation is in any way correct or that any incorrectness is proportional to the fault. If we could make such a guarantee, your point would stand, but the logic behind halting execution entirely by default is that we can make no such guarantees.
It is necessary, but not sufficient. Trapping certainly is not fault tolerant...
No, Ben, you're flipping my implication around. I'm not judging anybody's use of the term. I'm saying that the kind of “safety” referred to by the OP is not something we can supply as any kind of guarantee, and a trap that occurs because of the way you used
enumerate() is not in conflict with what we ever intended to promise by saying “swift is a safe language.” That's only an absolute truth w.r.t. memory safety, and yes, the kind of safety you say is defined in the dictionary is more casual because it can't be formally defined.
I don’t know of anyone making that claim.
To sum up, not trapping is not a form of fault tolerance, and it does not provide a “type of safety.”
I do think “lenience” is a fairly good term for this behavior, as it affords the user an opportunity to provide other forms of recovery. That lenience can be used well, resulting in some degree of fault tolerance, or it can be abused, resulting in even less safety. This possibility of either good or bad effect up to the end user is well captured, I think, by the idea of the behavior being described as “lenient.”
Trapping is NOT fault tolerant... thus knowing that something doesn't trap is necessary to provide fault tolerance.
You are forgetting that we are talking about the word "Safe" here. I am pointing out that if people are thinking of "safe" as fault tolerant (which is a reasonable assumption for those outside the SE discussions), then they are quite surprised when we classify trapping as "safe".
It isn't that not trapping provides a type of safety so much that trapping removes a form of safety.
To be clear, I am not arguing that we shouldn't trap... just that we need to understand why there has been confusion, find consistent language to talk about these different concepts, and then be clearer in our use of them. As @Nevin said, we need to educate.
Not so. It is not true that, but for trapping, Swift would be more fault tolerant. No form of fault tolerance is offered by not trapping in Swift, and no safety is removed by trapping.
That is indeed a pain point for some users. It is also the case that they are quite surprised that not trapping is unsafe (even in the sense of “not fault tolerant”), which in the general case (i.e., without dedicated alternative error handling, which requires anticipating the logic error, which could equally be done whether Swift traps or doesn’t trap) it is.
Please stop attributing your strawman arguments to me and then arguing against them instead of what I am actually saying. Please go back and take a minute to read what I actually wrote.
I’m not sure why you think I’m trying to attribute any arguments to you that you didn’t write, or that I’m trying to argue against you. I’m drilling down on specific points that I think could stand some elaboration in this conversation.