How can I change the value of Solana account with iterator? - rust

I wanna implement the feature that allow user to vote and change account value.
Now I get the error below.
cannot move out of dereference of `anchor_lang::prelude::Account<'_, BaseAccount>`
move occurs because value has type `Vec<ItemStruct>`, which does not implement the `Copy` trait
How can I change account value with iterator?
Here is the part of my code.
pub fn vote(ctx: Context<Vote>, gif_id: u64) -> Result<()> {
let base_account = &mut ctx.accounts.base_account;
let user = &mut ctx.accounts.user;
let gif_iter = base_account.gif_list.into_iter(); // error occur here
match gif_iter.find(|item| item.id == gif_id) {
Some(gif) => gif.votes += 1,
_ => (),
}
Ok(())
}

I solve this issue myself.
I fixed function like below.
pub fn vote(ctx: Context<Vote>, gif_id: u16) -> Result<()> {
let base_account = &mut ctx.accounts.base_account;
let gif_iter = &mut base_account.gif_list.iter_mut();
gif_iter
.find(|item| item.id == gif_id)
.map(|found| found.votes += 1);
Ok(())
}
into_iter() function move ownership of account to new variable. So I got the error.
I replace it with iter_mut() function. And also replacing match with map.

Related

Rust, returns a value referencing data owned by the current function

I checked forum for this question, I found some answers, but any of this situation was different and don't work for me.
I have problem with this error:
let test = PasswordHash::new(&hashed_password).clone().unwrap();
| ---------------- `hashed_password` is borrowed here
53 | // Ok(PasswordHash::new(&hashed_password).unwrap())
54 | Ok(test)
| ^^^^^^^^ returns a value referencing data owned by the current function
I exactly know what this error means. I know where the problem is, and I even know what provide it. But have no idea how can I fix it.
My idea it don't repeat part of code which is responsible for generating PasswordHash. I decided create separate function to do this.
My code below:
use argon2::{
password_hash::{
rand_core::OsRng,
PasswordHash, PasswordHasher, PasswordVerifier, SaltString, Error
},
Argon2,
};
struct AlgorithmData {
version: u32,
params: String,
algorithm_type: String,
}
impl AlgorithmData {
fn new() -> Self {
let example_string = "example_string";
let password_hash = Hasher::get_passwordhash_object(&example_string).unwrap();
AlgorithmData { version: password_hash.version.unwrap(),
params: password_hash.params.to_string(),
algorithm_type: password_hash.algorithm.to_string() }
}
}
pub struct Hasher {}
impl Hasher {
pub fn hash_string(string_to_hash: &str) -> Result<String, Error> {
let parsed_hash_password = Hasher::get_passwordhash_object(string_to_hash)?;
let result = format!("{}${}",
parsed_hash_password.salt.unwrap().to_string(),
parsed_hash_password.hash.unwrap().to_string());
Ok(result)
}
fn get_passwordhash_object(string_to_hash: &str) -> Result<PasswordHash, Error> {
let bytes_to_hash = string_to_hash.as_bytes();
let salt = SaltString::generate(&mut OsRng);
let argon2 = Argon2::default();
let hashed_password = argon2.hash_password(&bytes_to_hash,
&salt)?.to_string();
let test = PasswordHash::new(&hashed_password).clone().unwrap();
// Ok(PasswordHash::new(&hashed_password).unwrap()) --> here too is problem
Ok(test) // here is the same problem
}
pub fn compare_string_to_hash(string_to_compare: &String,
hash_to_compare: &String) -> Result<bool, Error> {
let bytes_to_compare = string_to_compare.as_bytes();
let bytes_from_hash = PasswordHash::new(&hash_to_compare).unwrap();
Ok(Argon2::default().verify_password(&bytes_to_compare,
&bytes_from_hash).is_ok())
}
}
Like you see main problem is variable: hashed_password and it reference in new object PasswordHash.
I was tried use copy function to clone hashed_password variable or object PasswordHash.
Second thing was trying to use lifetime parameter like <'a>.
Third idea was using Box::new to try access to memory directly.
I need help to resolve this problem. I have already read documentation. But still have no more ideas to resolve this.
Error number: E0515 explaining reason quite well.
You definitely have a lifetime issue here. In your case, the PasswordHash lifetime depends on the SaltString. So, if you want to send a reference of PasswordHash out to the caller, it's better to take SaltString as an argument to the function get_passwordhash_object.
Here is the minimal example derived from your code:
use argon2::{
password_hash::{rand_core::OsRng, Error, PasswordHash, PasswordHasher, SaltString},
Argon2,
};
pub struct Hasher {}
impl Hasher {
fn get_passwordhash_object<'a>(
bytes_to_hash: &'a [u8],
salt: &'a SaltString,
) -> Result<PasswordHash<'a>, Error> {
let argon2 = Argon2::default();
let hashed_password = argon2.hash_password(&bytes_to_hash, salt).unwrap();
Ok(hashed_password)
}
}
fn main() {
let salt = SaltString::generate(&mut OsRng);
let hash = Hasher::get_passwordhash_object("something".as_bytes(), &salt);
println!("{:?}", hash);
}
If you check the documentation for PasswordHash::new you will see that it returns a struct with the same lifetime of the parameter. In your case the the test variable lifetime depends on the lifetime of hashed_password, that itself depends on the lifetime of salt, that owns its data.
In the get_passwordhash_object, you can't return PasswordHash type and discard the salt, you need to return both, or transform the data in something else that owns its data.
One solution is to serialize the PasswordHash into PasswordHashString, because this type owns his data
pub fn hash_string(string_to_hash: &str) -> Result<String, Error> {
let parsed_hash_password =
Hasher::get_passwordhash_object(string_to_hash)?;
let result = format!(
"{}${}",
parsed_hash_password.salt().unwrap().to_string(),
parsed_hash_password.hash().unwrap().to_string()
);
Ok(result)
}
fn get_passwordhash_object(
string_to_hash: &str,
) -> Result<PasswordHashString, Error> {
let bytes_to_hash = string_to_hash.as_bytes();
let salt = SaltString::generate(&mut OsRng);
let argon2 = Argon2::default();
let hashed_password =
argon2.hash_password(&bytes_to_hash, &salt)?.to_string();
let test = PasswordHash::new(&hashed_password).clone().unwrap();
Ok(test.serialize())
}
pub fn compare_string_to_hash(
string_to_compare: &String,
hash_to_compare: &String,
) -> Result<bool, Error> {
let bytes_to_compare = string_to_compare.as_bytes();
let bytes_from_hash = PasswordHash::new(&hash_to_compare).unwrap();
Ok(Argon2::default()
.verify_password(&bytes_to_compare, &bytes_from_hash)
.is_ok())
}

Why am I allowed to have multiple &mut refs in nested functions (Rust)?

I'm new to rust, and am wondering why the following code doesn't result in a:
cannot borrow val as mutable more than once at a time error. It seems like by the time I've reached the second_layer function, I should have three separate references to the same original val variable:
val_ref in the main function body
val_ref2 in the first_layer function body
val_ref3 in the second_layer function body
Any help would be appreciated!
fn first_layer(val_ref2: &mut String)
{
*val_ref2 = String::from("first_layer");
println!("{}", val_ref2);
second_layer(val_ref2);
}
fn second_layer(val_ref3: &mut String)
{
*val_ref3 = String::from("second_layer");
println!("{}", val_ref3);
}
fn main()
{
let mut val = String::from("asdf");
let val_ref: &mut String = &mut val;
first_layer(val_ref);
println!("{}", val_ref);
}
Thanks,
References in a function calling another function temporarily do not exist for the duration of the function call.
Identifiers do not automatically live on in functions called further down the stack. Just because one function calls another does not mean that the callee should have access to the caller's local variables. You would get a not found in this scope if you tried.
What you can (try to) do is create a closure to capture the caller's environment and then reuse one of the caller's variables. But you will find that the compiler complains if you break the borrowing rules.
fn first_layer(val_ref2: &mut String) {
// println!("{}", val); // Cannot use val from main!
*val_ref2 = String::from("first_layer");
println!("{}", val_ref2);
(|val_ref3: &mut String| {
*val_ref3 = String::from("second_layer");
println!("{}", val_ref3);
println!("{}", val_ref2); // This is not allowed
})(val_ref2);
}
fn main() {
let mut val = String::from("asdf");
let val_ref: &mut String = &mut val;
first_layer(val_ref);
println!("{}", val_ref);
}
Another way to think about it is with inlining. If you inline your function second_layer into first_layer you get:
fn first_layer(val_ref2: &mut String)
{
*val_ref2 = String::from("first_layer");
println!("{}", val_ref2);
*val_ref2 = String::from("second_layer");
println!("{}", val_ref2);
}
This is totally fine!
So then the code with the last two lines abstracted to its own function should also be totally fine.

What is the origin of the error "cannot move out of borrowed content"?

I've never understood why I have received the Rust error "cannot move out of borrowed content".
use std::cell::RefCell;
use std::collections::VecDeque;
use std::rc::Rc;
use std::vec::Vec;
pub struct user_type {
pub name: String,
pub ilist: Vec<i32>,
pub user_type_list: VecDeque<Option<Rc<RefCell<user_type>>>>,
pub parent: Option<Rc<RefCell<user_type>>>,
}
impl user_type {
pub fn new(name: String) -> Self {
user_type {
name: name.clone(),
ilist: Vec::new(),
user_type_list: VecDeque::new(),
parent: Option::None,
}
}
pub fn to_string(&self) -> String {
let mut result: String = String::new();
result += "name is ";
result += &self.name;
let n = self.user_type_list.len();
for iter in &self.user_type_list {
match iter {
Some(ref x) => {
let temp = x.into_inner();
let temp2 = temp.to_string();
result += &temp2[..];
}
None => panic!("to_string"),
}
result += "\n";
}
result
}
}
The full error message is:
error[E0507]: cannot move out of borrowed content
--> src/main.rs:34:32
|
34 | let temp = x.into_inner();
| ^ cannot move out of borrowed content
What is the origin of this kind of error?
Look carefully at this code:
for iter in &self.user_type_list {
match iter {
Some(ref x) => {
let temp = x.into_inner();
let temp2 = temp.to_string();
result += &temp2[..];
}
None => panic!("to_string"),
}
result += "\n";
}
Here, you are iterating &self.user_type_list so the type of iter is actually a reference to the contained value: &Option<Rc<RefCell<user_type>>>. That is nice, because you do not want to take ownership of the container or its values.
Then you match iter to Some(ref x). Older compiler versions would fail because you are matching a reference to a non-reference, but new compilers will do as if you are matching a Option<&T> instead of a &Option<T>, if needed. That is handy, and means that you can write just Some(x) => and x will be of type &Rc<RefCell<user_type>> instead of &&Rc<..> (not that it really matters, automatic dereferencing will make those equivalent).
Now you are calling x.into_inner() with a &Rc<RefCell<..>> and that will never work. It looks like you want to get the RefCell into temp that is not needed, Rc implements Deref so you get that for free. Instead the compiler thinks you are calling RefCell::into_inner(self) -> T, but this function consumes the self to get to the contained value. And you do not own it, you just borrowed it. That is what the error message means: you are trying to consume (move out) and object you do not own (borrowd).
What you really want is just to borrow the user_type enough to call to_string():
Some(x) => {
let temp = x.borrow().to_string();
result += &temp;
}

error: `line` does not live long enough but it's ok in playground

I can't figure it out why my local var line does not live long enough. You can see bellow my code. It work on the Rust's playground.
I may have an idea of the issue: I use a structure (load is a function of this structure). As I want to store the result of the line in a member of my struct, it could be the issue. But I don't see what should I do to resolve this problem.
pub struct Config<'a> {
file: &'a str,
params: HashMap<&'a str, &'a str>
}
impl<'a> Config<'a> {
pub fn new(file: &str) -> Config {
Config { file: file, params: HashMap::new() }
}
pub fn load(&mut self) -> () {
let f = match fs::File::open(self.file) {
Ok(e) => e,
Err(e) => {
println!("Failed to load {}, {}", self.file, e);
return;
}
};
let mut reader = io::BufReader::new(f);
let mut buffer = String::new();
loop {
let result = reader.read_line(&mut buffer);
if result.is_ok() && result.ok().unwrap() > 0 {
let line: Vec<String> = buffer.split("=").map(String::from).collect();
let key = line[0].trim();
let value = line[1].trim();
self.params.insert(key, value);
}
buffer.clear();
}
}
...
}
And I get this error:
src/conf.rs:33:27: 33:31 error: `line` does not live long enough
src/conf.rs:33 let key = line[0].trim();
^~~~
src/conf.rs:16:34: 41:6 note: reference must be valid for the lifetime 'a as defined on the block at 16:33...
src/conf.rs:16 pub fn load(&mut self) -> () {
src/conf.rs:17 let f = match fs::File::open(self.file) {
src/conf.rs:18 Ok(e) => e,
src/conf.rs:19 Err(e) => {
src/conf.rs:20 println!("Failed to load {}, {}", self.file, e);
src/conf.rs:21 return;
...
src/conf.rs:31:87: 37:14 note: ...but borrowed value is only valid for the block suffix following statement 0 at 31:86
src/conf.rs:31 let line: Vec<String> = buffer.split("=").map(String::from).collect();
src/conf.rs:32
src/conf.rs:33 let key = line[0].trim();
src/conf.rs:34 let value = line[1].trim();
src/conf.rs:35
src/conf.rs:36 self.params.insert(key, value);
...
There are three steps in realizing why this does not work.
let line: Vec<String> = buffer.split("=").map(String::from).collect();
let key = line[0].trim();
let value = line[1].trim();
self.params.insert(key, value);
line is a Vec of Strings, meaning the vector owns the strings its containing. An effect of this is that when the vector is freed from memory, the elements, the strings, are also freed.
If we look at string::trim here, we see that it takes and returns a &str. In other words, the function does not allocate anything, or transfer ownership - the string it returns is simply a slice of the original string. So if we were to free the original string, the trimmed string would not have valid data.
The signature of HashMap::insert is fn insert(&mut self, k: K, v: V) -> Option<V>. The function moves both the key and the value, because these needs to be valid for as long as they may be in the hashmap. We would like to give the hashmap the two strings. However, both key and value are just references to strings which is owned by the vector - we are just borrowing them - so we can't give them away.
The solution is simple: copy the strings after they have been split.
let line: Vec<String> = buffer.split("=").map(String::from).collect();
let key = line[0].trim().to_string();
let value = line[1].trim().to_string();
self.params.insert(key, value);
This will allocate two new strings, and copy the trimmed slices into the new strings.
We could have moved the string out of the vector(ie. with Vec::remove), if we didn't trim the strings afterwards; I was unable to find a easy way of trimming a string without allocating a new one.
In addition, as malbarbo mentions, we can avoid the extra allocation that is done with map(String::from), and the creation of the vector with collect(), by simply omitting them.
In this case you have to use String instead of &str. See this to understand the difference.
You can also eliminate the creation of the intermediate vector and use the iterator return by split direct
pub struct Config<'a> {
file: &'a str,
params: HashMap<String, String>
}
...
let mut line = buffer.split("=");
let key = line.next().unwrap().trim().to_string();
let value = line.next().unwrap().trim().to_string();

How do I automatically clear an attribute in a struct when it is moved?

I have a struct
struct Test {
list: Vec<u64>
}
and method in which I would like to get vector and erase list field to empty Vec
fn get_list(&self) -> Vec<u64> {
let list = Vec::new();
for item in self.list.drain() {
list.push(item);
}
list
}
It there another approach for doing it? Something like autoreinit field on moving value, for example:
fn get_list(&self) -> ???<Vec<u64>> {
self.list
}
Here is the solution, you can test on Rust playground (sadly share button doesn't work for me atm).
use std::mem;
#[derive(Debug)]
struct Test {
list: Vec<u64>
}
impl Test {
fn get_list(&mut self) -> Vec<u64> {
let repl = mem::replace(&mut self.list, Vec::new());
repl
}
}
fn main() {
let mut r = Test {
list : vec![1,2,3]
};
print!("r : {:?} ", r);
print!("replace : {:?} ", r.get_list());
print!("r : {:?} ", r);
}
You just need to run mem::replace(docs) on a mutable value and replace it with a value that will be moved in its place. In this case our destination is self.list and value we are replacing it is a blank Vec.
Things to note:
Field self.list of Test, needs to be taken as &mut self.list.
Previous change implies that self should be mutable as well.
Second parameter of replace is moved. That means it won't be available for further after this call. What this usually means, you either pass it a Vec constructor (e.g. Vec::new()) or clone of value that's replacing.
From #rust IRC
< theme> jiojiajiu, http://doc.rust-lang.org/nightly/std/mem/fn.replace.html

Resources