>>25465356
And it's not an issue with C, but its implementations. And it's already fixed in at least a few of them. Although having scanf available at all is a big defect in C, the API is terribly broken. And the code that is calling scanf shouldn't be running in any loading screen in the first place.
Also, guess the runtime of scanning, printing, copying, or doing some operation on the whole string or any arbitrary position in it in UTF-8.
...linear.
Most programs don't just get the length of the string and are done with it, that's an useless operation, they operate on the string starting at some character position and up to the string's length or some other limit before the string's length. Operating on the string up until some limit is linear in both cases. So you made an operation that by itself is useless constant, but any time you do anything significant with the string it's still linear.
And what did you lose? Let's look at Rust:
https://doc.rust-lang.org/src/alloc/string.rs.html#362
The Rust `String` type is actually just a `Vec`. What's a `Vec` in Rust? It's a length and a `RawVec` according to https://doc.rust-lang.org/src/alloc/vec/mod.rs.html#397.
What's a `RawVec`? Following the code it says it has a `PhantomData` which is just a marker that occupies no space and a `RawVecInner` which is defined as a pointer to an array of bytes, the capacity, and a reference to the allocator. https://doc.rust-lang.org/beta/src/alloc/raw_vec.rs.html#88
So now your Rust string on a 64-bit architecture is at least 3 allocations: reference to Vec, the Vec itself, and the array of bytes Vec points to. 1 of these allocations (the array of bytes) has to be on the heap and incurs heap allocation overhead, and you have 8 bytes of length, 8 bytes of capacity, 8 bytes of pointer to the array of bytes, and however many bytes a reference to the allocator takes (I would hope it's a function pointer so 8 bytes). 32 bytes of overhead + 1 heap allocation overhead (it's variable but 16 bytes minimum on glibc, how many is it in Rust?) and 3 places in memory that most likely have no cache locality, but at least in a best case scenario the whole vec (except the array of bytes it points to) can be on 4 registers.
Now what's a C string on a 64-bit architecture? 2 allocations: a reference to the array of bytes, and the array of bytes itself which may or may not be on the stack, heap, or some data section and so it doesn't necessarily have heap allocation overhead. 1 byte for the terminator. That is, your overhead is 1 byte and you have 2 places in memory that most likely have no cache locality, but at least the reference to the array of bytes can be on 1 register.
Now let's see the runtime of string operations:
Parse a `key=value` pair:
C: O(n)
Rust: O(n)
Case fold a string:
C: O(n)
Rust: O(n)
Duplicate a string:
C: O(n)
Rust: O(n)
Read a string:
C: O(n)
Rust: O(n)
Write a string:
C: O(n)
Rust: O(n)
Convert a string to integer:
C: O(n)
Rust: O(n)
String length:
C: O(n)
Rust: O(1)
Are you going to write a program that gets the string length over and over until Rust is faster or are you going to write a real program that does some useful operation which has the same runtime complexity on both, but has better cache locality, less code, less memory overhead, and less register overhead in C?
Mind you, Rust has a simpler read-only `str` type which reduces overhead, but also introduces overhead because Rust code (including the stdlib) will use generics so it can accept any of `str`, `String`, `Path`, and `PathBuf` and that will produce several versions of the function in the machine code, further ruining cache locality and adding even more memory overhead, while C uses the same `char *` and the same code for all of that.
So which is faster? C.