Vec::split_off panics instead of returning an Option/Result!?

I was very surprised that the std library's split_off function could panic if the provided index is out of bounds (see Vec in std::vec - Rust).

Is there a design reason why this function doesn't return a Result or an Option? panicing seems very unidiomatic, and most other Vec functions seem to return Results/Options instead of panicing.

IMO that was an oversight. [T]'s split_at method also panics if the index is out of bounds, it's likely that the same contract was copied for consistency. Now it can no longer be changed though.

2 Likes

I'd say panicking API would have to take into consideration a few things like

  • how often would use-cases need/want to just call .unwrap() anyways because they already know that the failure is impossible for them?
  • how easy is it to do the check that prevents the panic manually before you call the API?
    • on that note, is it even an API that has the structure (1) check precondition (2) panic and do nothing else if precondition fails (3) do actual operation?
    • and how much overhead is it if the check is duplicated like this (since the second check for potentially triggering the panic doesn't go away)

With splitting of slices/vec, it's certainly quite common that your index calculations that determined the index you're interested in are done in a way that you know the index is going to be in-bounds anyways. Whether it's common enough to warrant the additional complication of needing manual checking for users that don't know the in-bounds-ness beforehand, is a different question. At least the index < v.len() check is cheap and easy to write (and likely optimized away if duplicated) and thus it's checking a lot of boxes in my above list for criteria when panicking behavior could be reasonable.

Another alternative to consider would be to offer both a panicking and a Option-returning API. This would e.g. be the case for indexing where we have subscript-operator (&v[ix]) as well as a non-panicking alternative (v.get(ix)) and this way the (possibly) more common case where you already know your index is in-bounds doesn't get super verbose (not v.get(ix).unwrap() but just &v[ix]).

Whether the current API for the split methods that take a usize index and panic on index out of bounds was a mistake or oversight, I'm not sure. I'd say it's a trade-off, and possibly one that would be decided the other way if the API was designed anew today ... but then again maybe not even that.

6 Likes

Yes. I think it is covered by this:

" Functions often have contracts : their behavior is only guaranteed if the inputs meet particular requirements. Panicking when the contract is violated makes sense because a contract violation always indicates a caller-side bug and it’s not a kind of error you want the calling code to have to explicitly handle. In fact, there’s no reasonable way for calling code to recover; the calling programmers need to fix the code."

https://doc.rust-lang.org/book/ch09-03-to-panic-or-not-to-panic.html

I would say an index out of range error indicates the calling code needs to be fixed. It is not a recoverable error.

It's like v[3..] not returning an Option.

Most of the time when you're doing indexing, you have an implicit algorithm invariant that the index is in-range. And thus you'd just be typing .unwrap() anyway.

Plus indexing is easy to check yourself before if you need it, and doing so will let LLVM optimize out the panic -- like like how assert!(x.len() > 10) will optimize out the bounds checks in x[0] + x[10].

3 Likes

Aww that's a pity. Usually the stdlib is very good at using Options/Results for failures

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.