Why do we have String::len?

Isn't String::len useless, since we already have str::len and String dereferences to str?

1 Like

impl Deref for String was added in #18443, which also removed several now-unnecessary inherent methods. One day after that PR was opened and two days before it was merged, #18474 was submitted, which removed the Collection trait, and added new inherent methods like String::len to replace the old Collection::len trait method.

It looks to me like this was simply an oversight caused by both of these PRs being in progress at the same time.

14 Likes

We also have Vec::len in addition to <[T]>::len.

Not exactly: https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=5d5614070a96417cad98a988c6e8d66c

fn foo(_f: impl Fn(&String) -> usize) {}

fn string_len(x: &String) -> usize { x.len() }
fn str_len(x: &str) -> usize { x.len() }

fn main() {
    foo(string_len); // Works
    foo(str_len); // ERROR: type mismatch in function arguments
}

The problem is that there are no function coercions. So something like .map(String::len) and .map(str::len) are different -- one can't just always use the more general one.

4 Likes

Yes, this is required for some unsafe code: v.deref().len() creates a shared reference to the entire current content of the vector; if you have outstanding pointers into the vector, they might be invalidated. v.len() avoids creating those references.

11 Likes

I would expect that creating a reference to the vector (e.g. &Vec<T>) should assert shared access to the whole slice, or at least to the first element since it is pointed by an Unique<T>. I would consider unsound to rely on Vec::len not invalidating outstanding mutable pointers.

You are expecting incorrectly then. :slight_smile: Unique cannot proactively assert anything since there might be no elements.

Vec even documents that you can rely on push not relocating the buffer under some conditions, so not invalidating outstanding pointers is clearly an explicitly intended feature of careful Vec interaction.

I was going to answer "likely pre-1.0 historical reasons", but felt it was too much of a guess. Turns out my guess was correct :grin:

Of course, removing the method now would be breaking, but that's not really the point.

Is that true? I thought that the dot operator would call len on str once not found on the String receiver.

That's true for the dot operator but it's breaking in other places: See Why do we have String::len? - #4 by scottmcm above

2 Likes

If rust supported methods with self: *const Self then <[T]>::len could be such a method (since it only accesses the metadata in the pointer).

If rust had some kind of DerefConstRaw trait which let you deref a *const to a *const then Vec<T> could implement this and allow you to get a *const [T] without acquiring a shared reference to the slice.

You'd then be able to write vec.len() without having to go through &[T] and without causing problems. I think (?).

2 Likes

@canndrew fully agreed; if Rust had those things, they would be helpful here.