问题
I'm having a play with Rust and, probably biting off far more than I can chew, am trying to write a module that will encapsulate my database traffic for the rest of the application. The code I'm struggling with is the following:
pub fn create_statement(cypher: &str, params: &HashMap<&str, &str>) -> rusted_cypher::Statement {
let mut statement = rusted_cypher::Statement::new(cypher);
for (field, value) in params.iter() {
statement.with_param(field.to_owned(), value.to_owned());
}
return statement;
}
This gives the following error: error[E0382]: use of moved value: statement
. My searching has, I think, lead me to what this means (the Statement
struct is not copyable, thus gets moved and is then ... effectively no longer accessible, I guess?), but I'm not sure how to get around it. Can anyone point me in the direction of a solution?
回答1:
I haven't used this API before but, according to its documentation:
This method consumes
self
and returns it with the parameter added, so the binding does not need to be mutable.
It isn't, as you say, that the struct is "not copyable", it's that the with_param
method is intentionally written in such a way that it moves the value and takes ownership - we can also say that it consumes it. Consuming the value is a common thing to do in a builder-style API because it prevents you from having accidental half-built objects lying around. Each builder method will consume the object, during which time nothing else can access it, and then return it when it's finished so you can continue using it. From the documentation:
let statement = Statement::new("MATCH n RETURN n")
.with_param("param1", "value1")?
.with_param("param2", 2)?
.with_param("param3", 3.0)?;
Each call to with_param
consumes statement
and then returns it so you can call with_param
again.
Where this gets a bit tricky is that the result of the with_param
is not a Statement
, but a Result<Statement, JsonError>
. Apparently adding a parameter could cause an error so the result is wrapped up to accommodate that possibility. That's what the ?
s are for - they unwrap that result into the underlying value, or else propagate the error if that can't be done.
As one of the other answers already suggested, a better fix is to just use the set_parameters
method, which will set them all in one go.
Another way to do it would be to use the return value of each call to with_param
:
pub fn create_statement(cypher: &str, params: &HashMap<&str, &str>) -> rusted_cypher::Statement {
let mut statement = rusted_cypher::Statement::new(cypher);
for (field, value) in params.iter() {
statement = statement.with_param(field.to_owned(), value.to_owned()).unwrap();
}
statement
}
When you use the return value, you have access to the value again, so you can keep on using it.
Notice that I used unwrap()
to get the value from the Result
. This is not good practice because it will cause a panic if the result is an error. I have done this here to focus on explaining move semantics without a digression into error handling, which is a topic by itself.
回答2:
While this doesn't answer your underlying question about passing a mutable value in the same way as your current code.
rusted-cypher::Statement
has a set_parameters
method which takes a &BTreeMap<String, T>
as its only argument.
You can see this function in the rusted-cypher's source code.
A conceivable implementation:
pub fn create_statement(cypher: &str, params: &BTreeMap<&str, &str>) -> rusted_cypher::Statement {
let mut statement = rusted_cypher::Statement::new(cypher);
statement.set_parameters(params);
statement
}
来源:https://stackoverflow.com/questions/44904677/passing-a-variable-to-a-function-which-alters-said-variable-repeatedly