I think this has been discussed before, but I wanted to raise it in case it hasn't, and perhaps make a more convincing argument for adding a startIndex like parameter to IndexOf family of methods in MemoryExtensions that operate on Span<T>.
The current recommendation, if I recall correctly, is that if you want to specify a start position, use Slice on the span prior to IndexOf.
Consider:
ReadOnlySpan<char> input = "no yes abracadabra no";
I want to find the contents between "yes" and "no", where the "no" occurs after "yes". This can be done with Slice, but the problem then is that IndexOf's result is relative to the Slice, not the actual contents. In a more complex scenario, this results in some gymnastics of "undoing" the slice to
int start = // the start position
int end = input[start..].IndexOf("no");
if (end == -1); // Handle
int realEnd = end + start;
Where ideally it's:
int start = //the start position
int realEnd = input.IndexOf("no", start);
if (realEnd == -1); //Handle
The problem here is that basically all of the stuff taking Span or Memory do not operate on offsets, so adding it just to IndexOf only helps minimally.
I do understand the problem though -- I've found myself doing Unsafe.ByteOffset as a way to as an efficient way to "map" a re-re-re-sliced Span into a Range that fits an original Span. It'd be nice to have an easier way to do this.
I do understand the problem though -- I've found myself doing Unsafe.ByteOffset as a way to as an efficient way to "map" a re-re-re-sliced Span into a Range that fits an original Span
This is basically my use case. #29588's API shape is to return Range's of characters from a ReadOnlySpan<char> while looking for a PEM, label, etc. These ranges are over the original caller supplied span, so any slicing needs to either be tracked as some kind of offset, or an API like the one here would significantly minimize the amount of "slice, check for -1, add offset from slice" work.
@GrabYourPitchforks
Since apparently I'm on a kick introducing Range to the public API surface...
int start = //the start position
int realEnd = input.IndexOf("no", start..);
if (realEnd == -1); //Handle
馃
@GrabYourPitchforks ha. I actually don't think that's a bad idea. Would solve my use case and give better flexibility and provide a "length"-like functionality in one go.
FWIW, I understand where you're coming from, and I've written the same code multiple times myself, but we've discussed this multiple times, and each time decided against it. Yes, you need to add the slice index to the result. But the alternative is a ton of new overloads that largely go against the whole idea of having spans that you can slice.
but we've discussed this multiple times
Perhaps that means it worth re-evaluating.
I hand-rolled this specific case as a private API and it made a remarkable difference in code quality and readability.
But the alternative is a ton of new overloads that largely go against the whole idea of having spans that you can slice.
I'm not suggesting Range, or startPosition is added everywhere Span is. I'm suggesting it for APIs that tell you about things _within_ the span. For things that _consume_ a span, slicing will continue to be the recommended approach.
Does the API tell you something about the contents of a Span by index? Offer a Range or position API. Otherwise, callers should use Slice.
Perhaps that means it worth re-evaluating.
I'm not understanding. I'm saying we've already re-evaluated it multiple times.
I'm saying we've already re-evaluated it multiple times.
Fair enough. It seems I am unlikely to get much traction here.
Most helpful comment
Since apparently I'm on a kick introducing
Rangeto the public API surface...馃