Whats with as_string()?


#1

Having spent a bit of time with Rust, I’ve gotten used to doing the translation between a String and a &str through using:

let foo = String::from_str("foo");
some_function(foo.as_string());

Which felt verbose, and wound up everywhere. After going through and cleaning up the latest round of warnings, all those have been replaced with:

let foo = String::from_str("foo");
some_function(&foo[]);

Which is certainly more terse. :smile: However, we’ve really just pushed food around the plate, and in some cases, made things look truly odd:

    for line in push_error.lines_any() {
        debug!("error: {}", line);
        if line.starts_with("remote") {
            let r = regex!(r"remote: (.+)");
            let caps_result = r.captures(line);
            match caps_result {
                Some(caps) => { sayln("white", &format!("{}", caps.at(1).unwrap())[]); },
                None => {}
            }
        }
    }

Check out the call to format! above. I’m pretty sure I could clean it up by making sayln generic, which I can do, but it really feels like we should be able to cast the String to an &str on demand. Is that the plan?


#2

In the latest nightly you should be able to convert a String to a &str just by using &:

sayln("white", &format!("{}", ...));

Sometimes it is not possible due to generics, but you could still invoke the dereference directly:

sayln("white", &*format!("{}", ...));

And the foo[] syntax is also going to be removed.


#3

That’s redundant. You’re creating a String (foo) from an &str, dereferencing foo (the String) into a str (dereferenced &str), and then creating a new String with .as_string().

let foo = String::from_str("foo");
some_function(&foo[]);

Here, you’re going from &str “foo” to String “foo” back to &str “foo”. Furthermore, some_function's signature must be different because it’s taking an &str not a String like the first case.


#4

Yeah, I know it’s redundant - it was a, perhaps bad, example. :wink:


#5

as_string is an alternate solution to the “hashtable string key String vs &str equivalence problem”. It’s kind of redundant now, but it’s only purpose is to be able to create a &String temporary from a &str without allocating anything.

Unfortunately the example in the documentation doesn’t make any sense (because its true use case doesn’t exist if you plan ahead).


#6

By that, do you mean "replaced with foo[..]"?


#7

No, &foo. This is called ‘deref conversions’


#8

s/conversion/coercion

And it will in fact be replaced by &foo[..], as &foo is not a perfect replacement for &foo[].

Consider the following code:

fn do_stuff_generic<T>(data: T) {}
fn do_stuff_slice(data: &[u32]) {}

fn main() {
  let foo = vec![1, 2, 3];
  do_stuff_generic(&foo); // passes in &Vec<T> because it is allowed
  do_stuff_generic(&foo[..]); // passes in &[T]
  do_stuff_slice(&foo); // passes in &[T] because of Deref Coercions
  do_stuff_slice(&foo[..]); // passes in &[T]
}

#9
do_stuff_generic(foo[..]);
do_stuff_slice(foo[..]);

Both of these pass in &[T], correct?


#10

Neither of those should compile since foo[..] has type [T] which is unsized. You need the & to get a fat pointer to what is otherwise an rvalue.

foo[..] is basically alternate syntax for *foo


#11

So the slicing operator does not return a slice, but an array? That’s surprising, but I get why it’s done that way.


#12

It’s not an array. See A contiguous storage by any other name for all the varieties.