Given "asdf".indexOf("a") is 0
Then "asdf"[0] is "a"
--
Given "".indexOf("") is 0
Then ""[0] is "" (which it's not in any language I know of)
Given "asdf".indexOf("a") is 0
Then "asdf"[0] is "a"
--
Given "".indexOf("") is 0
Then ""[0] is "" (which it's not in any language I know of)
but
"asdf"[0] is NOT "as".
so there can be no expectation that ""[0] is "".
Those two operations are not related to each other. It's more intuitive if you treat .indexOf() as .startOf(). Then "asdf"[0..x] is "as" for x=2, and ""[0..x] is "" for x=0.
If you're okay with using the same error code for (a) string not found and (b) erroneous usage, then returning -1 makes sense.
Since (b) is something that probably indicates a bug in the application code, I'd probably prefer that (b) triggers a different program flow. E.g., raising a C++ exception, failing an `assert`, etc.
In reverse, the function would be 'return the subsequence starting at index, for some length', which for "" and 0 would indeed be "" for length = 0.
Array indexing [] is unrelated
"asdf"[0:1] is "a"
and
""[0:0] is ""