Hi, by curiosity, we can't export a copy of a substring from a string defined inside a block. We get an error that explain that string doesn't live outside the block, but we just want a copy part of that string. How could we export a copy of a slice of local/inside string to make it available outside the block ? I already found workarounds.correction by using the heap (by string, not &str), or by defining variables outside and before the block.
// Test : export slice from a string inside a block
fn main () {
let mut version : & str = "0.0.0"; // default value
// let mut line = String::new(); // it works if "line" is already existant outside the block
for _i in [1..=2] { // some loop because of treatment
// build local(inside the block) string "line" from some function or whatever. Here it is just a given example.
let line = String::from(":v 1.2.3\r"); // or ":v1.2.3\r" (without space)
// line = String::from(":v 1.2.3\r"); // or ":v1.2.3\r" (without space) <-- it works (because defined outside and will still exists after the block)
// exctract "version" from "line" : = line(2..)
// ======= ERROR HERE ==========
// error[E0597]: `line` does not live long enough
// I would like to copy the part "1.2.3" from ":v 1.2.3"
version = line.get(2..).unwrap_or("").trim();
}; // line has ended here
println!("{}", version);
}
Compiling playground v0.0.1 (/playground)
error[E0597]: `line` does not live long enough
--> src/main.rs:15:19
|
8 | let line = String::from(":v 1.2.3\r"); // or ":v1.2.3\r" (without space)
| ---- binding `line` declared here
...
15 | version = line.get(2..).unwrap_or("").trim();
| ^^^^ borrowed value does not live long enough
16 |
17 | }; // line has ended here
| - `line` dropped here while still borrowed
18 |
19 | println!("{}", version);
| ------- borrow later used here
For more information about this error, try `rustc --explain E0597`.
error: could not compile `playground` (bin "playground") due to 1 previous error
&str isn't a string itself. It's only a view into some other string stored somewhere else. It's like a symlink in a filesystem.
For string literals, the real string storage is in your program's executable, but that's a special case.
You can't assign a newly-created not-yet-stored piece of text to &str, because &str has no place to store anything, and can't keep it. &str is also guaranteed to be read-only and immutable, so it can't overwrite anything either.
format! gives you a new String that owns its text. You must store it somewhere (in a variable or some container), as otherwise it will be dropped automatically.
Thanks for that detailed explanation. It is clear now.
&str is like a symling in a filesystem or a pointer to a part of String. The objet pointed by &str must exists long enough.
There are two things that complicate trying to understand Rust this way:
string literals ("these things") are a special case that doesn't behave the same way as typical &str references. They are built in into the program itself, which allows borrowing them for a special 'static lifetime that is infinitely long from the program's perspective. Although you can make new &'static references at run time by leaking memory, that ability isn't used often, because making new strings and never releasing them will eventually cause the program to run out of memory (or swap, or get terminated). So nearly all other strings will only be possible to borrow temporarily for some finite lifetime that isn't the 'static one.
Variables don't have to have an address. They don't even have to exist in the running program. Structs don't need to exist either, and don't need to have any particular layout – unless you take their address in a way that the optimizer can't ignore. If you print an address of something in Rust you don't learn where it was, you force Rust to make it have an address and make a pointer specifically for you to look at. It changes how the program is built, so this way you can't learn how things are actually done in the programs that don't inspect themselves. Rust doesn't treat addresses as meaningful for object identity, and can move them around and recycle them. It's more confusing for data types without indirection, but even with string literals you could see them being merged or optimized out (since they're compile time constants). And you need to be careful to look at the data address (which should be stable in practice) not the address of the fat pointer (which can be created out of thin air and placed anywhere).
When you assign to a &str variable, you're changing where the variable points to, but the text that it points to doesn't change, and can't change.
What I mean by that is that you can't create new text at run time (e.g. make a sentence by picking words or letters at random) by trying to write it to a &str. It points to some existing text that it can't change, and if you haven't allocated any storage for your new text, then you can't make it point to that storage either. In the symlink analogy, it's a symlink to a file on a read-only disk. If you edit the symlink to point to a different file on the read-only disk, it may seem like you can edit the content, but you can't actually create anything new. You can't replace a file on a read-only disk, and to make the symlink point to completely new data, you have to find some writeable disk first where you can create the file.