So there’s a few layers here, which make the String module not have such a function:
Elixirs String module is about utf8 encoded binaries. So it doesn’t provide an API for general binaries no matter the encoding of the bytes in it.
For utf8 encoded binaries people have already expressed that there’s multiple ways to “count” within utf8. The smallest unit in utf8 is a codepoint. Codepoints can be combined to form graphemes. Unrelated to a specific usecase you want to default to working with graphemes. I’d suggest this excelent blog post on the matter: The Absolute Minimum Every Software Developer Must Know About Unicode in 2023 (Still No Excuses!) @ tonsky.me.
Additionally while elixir could compute you a codepoint index or a grapheme index that wouldn’t be of much use to begin with. Given a binary and a grapheme/codepoint index there’s no performant way to find where that index is in the binary. Due to the nature that codepoints and graphemes have no fixed size there’s no way to seek to such indexes. One would need to walk the binary from the beginning parsing it for utf8 to figure out where the index is pointing to – and do it again if that had been done to calculate the index in the first place. Hence String.codepoints and String.graphemes, where you get a list. Having a list makes such indexes useable.
You can also count in bytes, but if you do that with utf8 you can easily separate bytes belonging to a single grapheme or codepoint leaving you with garbage.
But what if you explicitly want to deal with bytes or don’t even have utf8 in the first place. In this case you’d want to work with OTPs :binary module, which holds apis for arbitrary binaries. And unsurprisingly it has :binary.match/2 (binary — OTP 29.0.2 (stdlib 8.0.1)), which allows you to search a binary for occurances of a pattern and give you back results in the form of {byte_index, byte_length}. Bytes can be very easily be seeked to in binaries, so it’s fine to use that here.






















