Rust is an extremely different language from the others mainstream, And that differential factor is precisely what this question is about. Although somewhat "strange", this factor is also seen as one of Rust’s strongest points. Because of this sui generi, it is impossible to write a fully detailed answer, since it would have to be explained in full as the model of Ownership works in Rust.
Therefore, for a full understanding, it is ideal to read, in full, the chapter Understanding Ownership, from the official language book.
That said, a certain summary won’t hurt.
The system of Ownership in Rust is governed over three rules:
- Every value in Rust has one associated variable. This variable is called owner of its value.
- Each value can have only one owner. Heed
- When the variable (owner of a value) leaves the scope of a block, the value will be released.
Let’s look at the second item above. Although a value can have only one owner at a given time, there is no impediment to it transfer the Ownership of that value. That is, it is possible transfer possession (Ownership) value from one variable to another. This type of action is called move (the Ownership).
For example:
// A variável `s1` é dona da String:
let s1 = String::from("hello");
// A String foi movida da variável `s1`.
// A partir de agora, a variável `s2` é a dona.
let s2 = s1;
In this sense, we can reproduce an error similar to the question when trying to access the variable value s1
after being moved to s2
. If we tried to do this:
fn main() {
let s1 = String::from("hello");
let s2 = s1;
println!("{}, world!", s1);
}
The compiler would launch an error similar to this:
error[E0382]: borrow of moved value: `s1`
--> src/main.rs:5:28
|
2 | let s1 = String::from("hello");
| -- move occurs because `s1` has type `String`, which does not implement the `Copy` trait
3 | let s2 = s1;
| -- value moved here
4 |
5 | println!("{}, world!", s1);
| ^^ value borrowed here after move
This basically means that we try to access the value of a variable that no longer owns it. Obviously, as rule 2 postulates (a value can only have an owner), this would be a reason for error. If the value had its possession changed between variables, it is expected that the former owner is no longer valid - which justifies the error.
Thus, we understand that we cannot use a variable whose value was moved to try access a value.
But the value nay was moved in the question code... or was? (...)
Retaking the code problematic of the AP:
use fancy_regex::Regex;
fn main() {
let text = "...";
let re = Regex::new(r"\b(m|d)[a-z]+").unwrap();
let captures = re.find_iter(text);
for cap in captures {
let begin = cap.unwrap().start() - 10;
let fim = cap.unwrap().end() + 10;
println!("{}", &text[begin..fim]);
}
}
It is possible to notice (by the error message of the question) that the error occurs in view that the value of the variable cap
is trying to be accessed after being moved.
In the situation in question, cap
is the type std::result::Result<Match, Error>
. Let’s see now the signature of the method unwrap
:
pub fn unwrap(self) -> T
Note that the value self
is being used so as to take the Ownership of the instance. This means that a modification of Ownership implicit. Pay attention to self
- if it were &self
or &mut self
, the story would be different; but we will not deal with it here not to extend much.
That is, the method unwrap
of Result
s take for themselves the possession of the value, which is "totally consumed". This means that the variable that contained the Result
can no longer be "read", since the value had its ownership changed. Think that the possession was for a variable within the method unwrap
(this variable is the parameter self
).
Therefore, to correct this, just call unwrap
only once, so the variable is no longer accessed later:
let re_match = cap.unwrap();
let begin = re_match.start() - 10;
let fim = re_match.end() + 10;
// ...
Note that now the variable cap
, single for each iteration of the for
, is no longer used after possession of its value has been moved to the parameter self
of the method unwrap
.
According to chapter 5.3, [Struct] Method Syntax:
Having a method that takes Ownership of the instance by using just self
as the first Parameter is Rare; this technique is usually used when the method Transforms self
into Something Else and you want to Prevent the Caller from using the original instance after the Transformation.
I mean, a method that takes the Ownership of the instance (using self
as first argument) is rare. This technique is usually used when the method transforms self
on something else and there is the desire to prevent the caller from using the original instance after the transformation.
And this need makes sense in the case of unwrap
, so that the Ownership of the instance of Result
is taken after calling the method in question.
Again, I insist that the reading of the chapters mentioned in this answer are of paramount importance so that really is understood as the system of Ownership works in Rust. What I said here is just a summary. I didn’t even mention anything about borrowing, which is also important in this field.
I know I’m not supposed to use the commentary for this, but congratulations on the answer!
– Lucas