.len() is not the length of a slice induced by the range or the distance between start and end but instead it's the len method form `ExactSizedIterator`, which in turn is a "special case" of where `Iterator.size_hint()` is known to return a correct value.
The thing is `Iterator.size_hint()` does return a size, which is usize.
So `Range<u64>` can only implement `ExactSizedIterator` on 64-bit targets, which I guess is why someone decided that it's better to not implement it (at all) to not hinder portability of libraries as it would be quite easy to accidentally write a lib not working on 32 bit. Not sure if that is the right decision tbh.
It is an interesting, awkward edge case. But the utility of a u64 length might be rarer than you think. `Range<usize>` will be more common for wrangling memory sizes, and will handle 64-bit sizes on 64-bit targets - so the utility of Range<u64> would mainly be for larger-than-address-space sizes, which probably means falliable I/O calls.
I've been considering upstreaming a trait into the read_write_at crate to provide std::io::Result<u64> lengths for Mutex<impl std::io::Seek> / std::fs::File (on platforms where length is available without mutating seek position - such as on windows via file.metadata().map(|m| m.file_size()) per:)
There's a whole slew of u64 offsets and sizes... the occasional subtraction to calculate a maybe-larger-than-memory size doesn't seem like that big a deal. Occasionally there are methods implemented for it - typically named "file_size()" instead of "len()" though.
This is another Rust design mistake: conflating the platform pointer size with "lengths". It's possible to process files bigger than 4 GB on 32-bit platforms. It's even possible to memory map them (using sliding windows/views).
In some Rust forums, the language designers were shocked to learn that this is possible.
At any rate, the Range type was very clearly designed for slices of in-memory contiguous arrays. Then template magic was used to make them "generic over all types". So now we have a situation where Range acts like an array subset and a B-Tree range selector, and a source for ordinals in iteration, and a bunch of other things. Some of which are incompatible.
Using pointer-sized sizes for in-memory collections and slices is no mistake, and every single Rust API involving file offsets or sizes in the Rust stdlib that I'm aware of uses u64 or i64, not pointer sized stuff. Perhaps you didn't mean to imply otherwise, but you do.
That iterators and ranges have this 1% edge case where you'll need to roll your own trait if you want "(0u64..1).len()" to compile because ExactSizedIterator was designed for the 99% case of in-memory collections, could be argued as a design mistake - or could be argued as a reasonable avoidance of overcomplication in std in favor of allowing the end users who encounter that edge case to solve the problem how they please, if it is indeed a problem for them.
Meanwhile, I'm still inheriting C/C++ codebases using APIs that know they're dealing with files and still use pointer-sized integers. In their defense, the system APIs they use often predate widespread 64-bit integer support in 32-bit C compilers (I'm looking at you, fseek/ftell). Less in their defense, the wrappers around said system APIs often postdate the very same, and postdate a slew of alternative APIs that don't even need 64-bit integer support.