Ung. I love in many ways, but the String/&str thing often wrecks my head. Why is there &str *and* &String? Making functions they can take either type is annoying. (currently trying to wrap my head around Borrow vs AsRef).

@ebel i've been making my functions accept "impl AsRef<str>" which means any type that has an ".as_ref::<str>()" can be passed in directly - you just have to call .as_ref() in the function body to get at the &str.

@lifning yeah I know about that technic and have used it. Maybe I'm doing something complicated which is _too_ generic πŸ€”. I don't want to limit my code to Strings, but generic types. So I need to wrap my head around Borrow, like HashMap

@ebel I've been thinking of &str as like a slice ref for characters, while &String is like a &Vec of same.

I kinda wish it'd been called &[char] instead of &str... (but that would probably be wrong in some detail, like it's not indexable so can't be a slice? ugh!)

@brion oh wow `&[char]` I didn't think of that. That's another thing that's like a &str.

I presume (hope?) there's some good reason that's clear when you know enough rust.

@ebel my sense of it is that it's most flexible to take a &str when you're borrowing input, and a String when you're taking ownership.

You can always make a &str from a String at no runtime cost, but not the other way.

A big advantage of &str, like slices, is that they can be a substring of a larger string in memory. So for instance a text-file parser might be able to pass around substrings from a source buffer "for free" without copying them, and then at the point you need a copy you create a String.

@brion oh the whole "return part of a big parsed string" does sound like a great advantage.

I'm fine with the concept of slices (and slices of slices), it's just the fact that `String`/`str` are different types! 😞

@ebel @brion This post and the next one explain this nicely:

A nice one on AsRef -

&str is not plainly indexable because it stores strings in UTF-8 encoding, so an arbitrary index may not point to the beginning of a character. You *can* slice a &str in constant time after knowing valid indices, which you get from its methods.

&[char] would be a slice of whole 32-bit characters, not as compact as UTF-8. You can index that one arbitrarily.

@federicomena @brion The 2nd is a really good post.

My problem is that I'm writing an API that's generic across types and I want it to take Str(ing)s as well as other types. If I was just writing something for strings, I could just use `impl AsRef<str>` etc.

@ebel @brion I'm not sure what your API looks like, but it sounds like if you want your constructor to work for both of

Foo::new("string slice")
Foo::new(String::from("owned string"))

Maybe you want it to take <T: ToOwned> ?

@ebel It's because they're two completely different types. It's like the difference between a PathBuf and a &Path, or a Vec<u8> and a &[u8].

The String is essentially a StrBuf, whereas a &str by itself is an instance of a view into a string, which may be managed by a String.

&String will contain capacity information in addition to length. &str is just a slice to a collection of bytes -- contains a pointer to the first element in the string, and the length of the string.

@ebel Technically, a `&str` is a `&[u8]` which was validated to be UTF-8. A `&[char]` is not a valid UTF-8 representation.

@ebel I usually opt for &str for arguments and String for return values. The borrower pattern helps make maintaining memory a bit more sane.

Sign in to participate in the conversation

The social network of the future: No ads, no corporate surveillance, ethical design, and decentralization! Own your data with Mastodon!