Ergonomics of creating `String`s

Very true. Unfortunately there is pretty much no way around it. Even taking an iterator, you need to first allocate a Vec to be able to call into_iter(). The problem here is that you can't move out of slices. I ocassinoally wish we had into_iter() for [T; N].

Those guidelines shouldn't be taken for gospel. They still require a lot of work and they were written before IntoCow<'a, str> (or its newer replacement, Into<String>) was added to std.

Into<String> provides precisely this benefit with the added advantage that the caller doesn't have to explicitly call to_string or to_owned or into. (And no allocations are hidden.)

3 Likes

[quote="Florob, post:21, topic:850"]
Very true. Unfortunately there is pretty much no way around it. Even taking an iterator, you need to first allocate a Vec to be able to call into_iter(). The problem here is that you can't move out of slices. I ocassinoally wish we had into_iter() for [T; N].
[/quote]Ah, so this is a complication of passing owned data. I see now. Yeah, too bad.

Good and fair point. I think it depends on the way you slice the argument. If you really care about seeing the allocation just looking at the call site it doesn't matter which trait bound you use. If you assume the prototype is also known, I fully agree with you.

[quote="Florob, post:21, topic:850"]
I occasionally wish we had into_iter() for [T; N].
[/quote]What was the reasoning for not having it?

Requires better destructor control for less overhead. If it were simple, it'd exist. That said, it's still possible and the crate with "literator" implements it, and I've implemented it in toy code myself too.

1 Like

First of all thanks, everyone for the great discussion and ideas.

So… I've now tried to migrate my library to take parameters generic over Into<Cow<'static, str>>.
The effect is a bit unfortunate, since now I end up with code like this:

Element::new("b", None::<&str>, Vec::<(&str, Option<&str>, &str)>::new())

I.e. for empty vectors and Option::None variants the types can't be inferred for obvious reasons. I've also tried defaulting the type arguments, but that doesn't seem to help. Any ideas?

4 Likes

Unfortunately I don't think there is a way to have default function type parameters in Rust as required to not have to specify types of None/empty vectors. I may have missed something, but this seems to be a weak point of generics, something that doesn't exist yet.

I've found this frustratingly unergonomic as well, mainly in dealing with unit testing of functions which output Strings or Vecs, and so on.

I agree with the OP. It would be nice to have a more convenient syntax for working with Strings. C++ has its caveats, of course, but the implicit conversion from char* to std::string is very convenient. It's up to the library users to read the documentation and understand the performance implications of calling a function which takes a String, if it matters for their use case.

You can also do "hello".into() now, if the expected type is a String.

I created a thread about this in the internals forum: Pre-RFC: `String` literals through prefixes - language design - Rust Internals