If immutable and mutable slices are differently typed [2], it is natural to define two functions (say, `slices.Compact` vs. `slices.Compacted`) to handle each type, like Python `list.sort` vs. `sorted`. It should be natural to expect `slices.Compacted` to never alter its input, and any attempt to use a mutable version will be very explicit except for slicing [1].
That would be nice, sure, but it still doesn't fix the problem for mutable slices. Immutable slices are a distraction - this discussion is explicitly about how the mutable version is supposed to work. Unless you want to argue that mutable arrays/slices shouldn't exist at all, of course.
This would not allow the previous errors to be checked by the compiler since the main thing you're relying on is the name. Nothing prevents you to call deleted(mutable) and discard the result apart from the name.
Indeed. Though, Rust does have a way to mark a function such that any caller that implicitly discards its return value gets a compiler warning. This feature largely solves the problem you’re talking about. But it’s orthogonal to Rust’s borrowing system or mutable versus immutable distinction.
That said, I’d also point out that, while you can more or less replicate the Go example with Rust slices, in Rust it would be more idiomatic to pass around a Vec (or a mutable reference to a Vec) if a callee needs to do something like change the length. And you can’t resize a Vec if there are other references to its contents.
> Nothing prevents you to call deleted(mutable) and discard the result
The Go compiler generates an error when you are (silently) ignoring the return value of any function.
Or, to put it in other words, every compiler which does allow to (silently) ignore the return value of a function, should not be used at all (C++ has at least `[[nodiscard]]` since 17 and C with C23 - which is "too little and too late", as always).
While that's a valid concern, it is an orthogoal issue as it can be similarly replicated in Rust as well. Rust references always track mutability but we can sidestep that by using `std::borrow::Cow`:
Then it is clear that, for example, `compacted(vec![...].into());` as a statement will exhibit the same behavior because `Cow` doesn't have `#[must_use]`. Rust avoids this issue mainly by encouraging explicitly mutable or immutable values by default, and at this point the language should have substantially altered that Go can do the same.
If immutable and mutable slices are differently typed [2], it is natural to define two functions (say, `slices.Compact` vs. `slices.Compacted`) to handle each type, like Python `list.sort` vs. `sorted`. It should be natural to expect `slices.Compacted` to never alter its input, and any attempt to use a mutable version will be very explicit except for slicing [1].
[1] Especially given that the capacity is preserved by default, which contributes to the current confusion. See my older comment: https://news.ycombinator.com/item?id=39112735
[2] Originally "...are different" but edited for clarity.
That would be nice, sure, but it still doesn't fix the problem for mutable slices. Immutable slices are a distraction - this discussion is explicitly about how the mutable version is supposed to work. Unless you want to argue that mutable arrays/slices shouldn't exist at all, of course.
This would not allow the previous errors to be checked by the compiler since the main thing you're relying on is the name. Nothing prevents you to call deleted(mutable) and discard the result apart from the name.
Indeed. Though, Rust does have a way to mark a function such that any caller that implicitly discards its return value gets a compiler warning. This feature largely solves the problem you’re talking about. But it’s orthogonal to Rust’s borrowing system or mutable versus immutable distinction.
That said, I’d also point out that, while you can more or less replicate the Go example with Rust slices, in Rust it would be more idiomatic to pass around a Vec (or a mutable reference to a Vec) if a callee needs to do something like change the length. And you can’t resize a Vec if there are other references to its contents.
4 replies →
> Nothing prevents you to call deleted(mutable) and discard the result
The Go compiler generates an error when you are (silently) ignoring the return value of any function. Or, to put it in other words, every compiler which does allow to (silently) ignore the return value of a function, should not be used at all (C++ has at least `[[nodiscard]]` since 17 and C with C23 - which is "too little and too late", as always).
3 replies →
While that's a valid concern, it is an orthogoal issue as it can be similarly replicated in Rust as well. Rust references always track mutability but we can sidestep that by using `std::borrow::Cow`:
Then it is clear that, for example, `compacted(vec![...].into());` as a statement will exhibit the same behavior because `Cow` doesn't have `#[must_use]`. Rust avoids this issue mainly by encouraging explicitly mutable or immutable values by default, and at this point the language should have substantially altered that Go can do the same.
1 reply →
This sounds awful in practice having to memorize different functions for the same thing based on mutability of the thing.
In practice it's not that bad
Programming is hard and one can not destroy complexity, only move it; other news at 12.