Understanding working with type &str

How to change my code for v: Vec<&str>

fn main() {
    
    // let mut v: Vec<&str> = Vec::new();
    let mut v: Vec<String> = Vec::new();

    let mut do_it = |x: &str, y: &str| {

        v.push(x.to_string() + " -> " + y);
    };

    do_it("aaa", "bbb");
    do_it("ccc", "ddd");

//
for e in v.iter() {
    println!("{}", e);
}
println!("v = {:?}", v);  //  v = ["aaa -> bbb", "ccc -> ddd"]
}
like this - doesn't work:
fn main() {
    
    let mut v: Vec<&str> = Vec::new();
    // let mut v: Vec<String> = Vec::new();

    let mut do_it = |x: &str, y: &str| {

        v.push(&(x.to_string() + " -> " + y));
    };

    do_it("aaa", "bbb");
    do_it("ccc", "ddd");

//
for e in v.iter() {
    println!("{}", e);
}
println!("v = {:?}", v);
}

Such question on the topic:
Why can't concatenation be implemented
while preserving the original address?

for example:
fn main() {
    
    let s: &str = "x";
    println!("1. {:?}", s);     // "x"
    println!("1. {:p}\n", &s);  // 0x7ffd82dd8b70

    let s: &str = &(s.to_string() + "y" + "z");
    //
    println!("2. {:?}", s);    // "xyz"
    println!("2. {:p}", &s);   // 0x7ffd82dd8c28 <-- new address 
    // is it possible to concatenate all terms of type &str
    // with the result at the address of the first ?
}

What you're asking is not possible, you cannot modify the data pointed by a &str, let alone append to it.

What are you actually trying to do?

By the way you are printing the address of the stack variables named s. If you want to print the address of where the string data is stored you'll have to print just s, not &s.

1 Like

I don't understand why it's implemented this way

saving the original address is more efficient:
    let mut s: &str = "x";
    println!("1. {:?}", s);     // "x"
    println!("1. {:p}\n", &s);  // 0x7ffd82dd8b70
    //
    s = "qwerty_string";
    //
    println!("2. {:?}", s);    // "qwerty_string"
    println!("2. {:p}", &s);   // 0x7ffd82dd8b70 <-- not changed 

Thank you, good point.

  • it's not really that more efficient because the string is still getting a new allocation. It's just the local variable that's reused.

  • in practice the compiler can reuse the stack space for local variables that are no longer used, but you won't see this if you print their addresses because that inhibits some optimizations due to the address being exposed.

2 Likes

Thank you.
For my closure example (like this - doesn't work) with
let mut v: Vec<&str> = Vec::new();
how to fix it correctly ?

fn main() {
    let mut v: Vec<String> = Vec::new();

    let mut do_it = |x: &str, y: &str| {
        v.push(x.to_string() + " -> " + y);
    };

    do_it("aaa", "bbb");
    do_it("ccc", "ddd");

    let v: Vec<&str> = v.iter().map(|s| s.as_str()).collect();

    for e in v.iter() {
        println!("{}", e);
    }
    println!("v = {:?}", v); //  v = ["aaa -> bbb", "ccc -> ddd"]
}

You can't avoid creating the Vec<String> or something equivalent.

2 Likes

more efficient

No, non, pas du tout, nein, not at all.

This is a widespread misconception. References aren't magic. They aren't intrinsically "more efficient" than owned types. They serve different purposes.

The way to use references to make code more efficient is to avoid cloning when passing read-only data to functions. If you already allocated an owned String, you can't retroactively "undo" the cost of allocation by using a reference. That makes no sense.

3 Likes

Thanks

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.