pattern matching borrowed content issue - rust

So I have this piece of code that reads input.csv, inserts a column in it and writes it to output.csv
extern crate csv;
use std::path::Path;
struct User {
reference: String,
email: String,
firstname: String,
lastname: String
}
fn main() {
let mut rdr = csv::Reader::from_file("/tmp/input.csv").unwrap().has_headers(false);
let mut wtr = csv::Writer::from_file(Path::new("/tmp/output.csv")).unwrap();
let users = get_users();
for record in rdr.decode() {
let rec: Option<Vec<String>> = match record {
Ok(rec) => Some(rec),
Err(e) => None
};
match rec {
Some(mut r) => {
let usr = users.iter().find(|&ur| ur.reference == r[27].to_string());
match usr {
Some(u) => r.insert(1, u.email),
None => r.insert(1, "Unknown".to_string())
}
wtr.write(r.iter());
}
None => {}
};
}
}
fn get_users() -> Vec<User> {
//retrieve users
}
and it's giving me an error:
error: cannot move out of borrowed content
Some(u) => r.insert(1, u.email),
^
So I understand it's getting upset about u.email, because r is trying to take ownership of it(?), but how to best handle such a situation?

Here is a slightly simplified portion of your code which demonstrates the problem:
struct User {
reference: String,
email: String
}
let users = vec![
User { reference: "1".into(), email: "a#a.com".into() },
User { reference: "2".into(), email: "b#b.com".into() }
];
let records: Vec<Vec<String>> = vec![
vec!["1".into()],
vec!["2".into()],
vec!["3".into()]
];
for mut record in records {
let usr = users.iter().find(|ur| ur.reference == record[0]);
match usr {
Some(u) => record.insert(1, u.email),
None => record.insert(1, "Unknown".into())
}
// do whatever with record
}
usr in let usr here is of type &User, not User, because iter() returns an iterator satisfying Iterator<Item=&User>, and hence find() returns Option<&User>. Consequently, you cannot take a String out of u: &User - you can't move out of a borrowed reference. This is, BTW, an absolutely correct error - if this was allowed, your code would break in a situation with multiple records corresponding to the same user (it would require moving the email out of the same user multiple times).
The most natural way here would be just to use clone():
record.insert(1, u.email.clone())
It would create a copy of the email string contained in the found User, exactly what you need.

Related

How to update the existing todo item in Rust language

Currently building a CRUD system and want to replace the selected item with the new updated item value. Since I am a noob, still need to learn lot of things, so how can I fix this. Completely confused on how to fix that.
What the current problem is that I am not able to find the item name for example bob and replace that with a new item value.
let action = std::env::args().nth(1).expect("Please provide an action");
let item = std::env::args().nth(2).expect("Please provide an item");
let _getitem = std::env::args().nth(3).expect("Please provide an item");
struct Todo {
map: HashMap<String, bool>,
}
if action == "edit" {
match todo.edit(&item, &_getitem) {
None => println!("'{}' is not present in the list", item),
Some(_) => match todo.save() {
Ok(_) => println!("todo saved"),
Err(why) => println!("An error occurred: {}", why),
},
}
}
fn edit(&mut self, key: &String, value: &String) -> Option<()> {
let elements = self.map.get_mut(key);
elements.push(value.to_string());
}
Data structure of hashmap looks like:
{"bob": true, "new": true }
I assume you want to update an existing record in a HashMap. One way to go about this is using HashMap::entry (Rust Doc):
Once you select an entry you can modify it using the Entry API. This allows you to chain updates and inserts, for example:
let mut map: HashMap<&'static str, usize> = HashMap::new();
map.insert("a", 1);
map
.entry("a")
.and_modify(|val| *val += 1);
In your case edit can look like this:
fn edit(&mut self, key: &String, value: bool) {
self.map.entry(key)
.and_modify(|val| *val = value);
}
Note that in your example the value of a HashMap must be bool but you are trying to update it to a string. You need to parse this first, e.g. using from_str (Rust Doc) or by using your own function:
fn to_bool(s: &str) -> Result<bool, ()> {
if s == "x" {
return Ok(true);
}
if s == "" {
return Ok(false);
}
Err(())
}

how do you borrow a value during match in Rust?

I'm a new to Rust.
I created a structure to hold system information.
pub struct systemConfig {
pub admin_address: String,
pub engine_name: Option<String>,
pub group_name: Option<String>
}
I want to pass this structure to the make_msg function to create a json body and send it as a request to another server.
fn make_msg(config: systemConfig) -> String{
let (host_name, cpus) = get_system_info();
let engine_name = match config.engine_name {
Some(name) => name,
None => host_name.clone(),
};
let group_name = match config.group_name {
Some(name) => name,
None => String::from("")
};
let msg = json!({
"engineName": engine_name,
"groupName": group_name,
"hostName": host_name,
});
msg.to_string()
}
fn get_system_info() -> (String, usize){
use sysinfo::{ System, SystemExt };
// monitoring info
let mut my_system = System::new_all();
my_system.refresh_all();
// hostname
let hostname = get_hostname(&my_system);
// logical cpu count
let cpus = get_logical_cpus(&my_system);
(hostname, cpus)
}
I have two questions.
engine_name and group_name are values ​​obtained from process argument. The reason that type is defined as Option is that its value is not required. If the engine name is not entered, the hostname is filled in. And If the group name is not entered, it is sent as ""(empty String).
I used the match syntax, but is there a more appropriate syntax? (if let Some/None,,
more concise and intuitive)
None => host_name.clone(),
If clone() is not performed here, a borrow issue occurs. I'm looking for advice on whether using clone() is the right way, or if there is a better way.
I add test code
//cargo.toml
[dependencies]
sysinfo = "0.23.12"
serde_json = { version = "1.0", features = ["arbitrary_precision"] }
use sysinfo::{System, SystemExt};
use serde_json::json;
struct systemConfig {
pub admin_address: String,
pub engine_name: Option<String>,
pub group_name: Option<String>
}
fn main() {
let config = systemConfig {
admin_address: String::from("127.0.0.1:8080"),
engine_name: Some(String::from("hello")),
group_name: Some(String::from("world"))
};
let msg = make_msg(config);
println!("msg: {}", msg);
}
fn make_msg(config: systemConfig) -> String{
let host_name = get_system_info();
let engine_name = match config.engine_name {
Some(name) => name,
None => host_name.clone(),
};
let group_name = match config.group_name {
Some(name) => name,
None => String::from("")
};
let msg = json!({
"engineName": engine_name,
"groupName": group_name,
"hostName": host_name,
});
msg.to_string()
}
fn get_system_info() -> String {
use sysinfo::{ System, SystemExt };
// monitoring info
let mut my_system = System::new_all();
my_system.refresh_all();
// hostname
let hostname = get_hostname(&my_system);
hostname
}
pub fn get_hostname(s: &System) -> String {
s.host_name().unwrap()
}
I used the match syntax, but is there a more appropriate syntax? (if let Some/None,, more concise and intuitive)
Option has a few utilities that you could use. In the engine_name case, unwrap_or_else() is less verbose than your match:
let engine_name = config.engine_name
.unwrap_or_else(|| host_name.clone());
For group_name you can use unwrap_or_default() since the Default implementation on String returns an empty string:
let group_name = config.group_name.unwrap_or_default();
Note that both of these options are superior in this case to unwrap_or() since they do not require building the alternative value unless it's needed. For example, in the engine_name case this won't clone host_name unless config.engine_name is None.
I'm looking for advice on whether using clone() is the right way, or if there is a better way.
You can make it work using only references like this:
let engine_name = match &config.engine_name {
Some(ref name) => name,
None => &host_name,
};
Or, like above, you can use unwrap_or() (combined with as_ref()):
let engine_name = config.engine_name.as_ref().unwrap_or(&host_name);
However, the JSON Value::String variant requires an owned string, so not cloning here isn't really an optimization -- the json! macro will just clone it anyway.
Two questions, two answers:
The situation of unwrapping or replacing an Option is common enough that it got its own function: Option::unwrap_or:
let engine_name = config.engine_name.unwrap_or(host_name.clone());
let group_name = config.group_name.unwrap_or(String::from(""));
Clone is the right way. In some situations, engineName and hostName will contain the same string, so a .clone() will be required at some point either way.

Convert Vec<String> to std::rc::RC<Vec<String>>

I have a function that receives a list of ids and then selects them from a database. I'm passing in a Vec and I found this issue https://github.com/rusqlite/rusqlite/issues/430 which linked to here https://github.com/rusqlite/rusqlite/blob/master/src/vtab/array.rs#L18 and it says // Note: A Rc<Vec<Value>> must be used as the parameter.
I cannot figure out how to convert this Vec to Rc<Vec> is a way that does not produce a compile error. I tried the following:
let values = std::rc::Rc::new(ids.into_iter().copied().map(String::from).collect::<Vec<String>>());
let values = std::rc::Rc::from(ids.into_iter().map(|item| item.to_string()).collect::<Vec<String>>());
let values = std::rc::Rc::from(&ids);
All 3 give the same error with some variation of this part Vec<Rc<Vecstd::string::String>>
the trait bound `Vec<Rc<Vec<std::string::String>>>: ToSql` is not satisfied the following implementations were found: <Vec<u8> as ToSql> required for the cast to the object type `dyn ToSql`
How can I convert this so it comes out as Rc<Vec> and not Vec<Rc<Vec>>
My code is here
fn table_data_to_table(ids: &Vec<String>) -> Vec<data::Item> {
let db_connection = rusqlite::Connection::open("data.sqlite")
.expect("Cannot connect to database.");
let values = std::rc::Rc::new(ids.into_iter().copied().map(String::from).collect::<Vec<String>>());
let mut statement = db_connection
.prepare("select * from item where id in rarray(?);")
.expect("Failed to prepare query.");
let mut results = statement
.query_map(rusqlite::params![vec![values]], |row| {
Ok(database::ItemData {
id: row.get(0)?,
name: row.get(1)?,
time_tp_prepare: row.get(2)?
})
});
match results {
Ok(rows) => {
let collection: rusqlite::Result<Vec<database::ItemData>> = rows.collect();
match collection {
Ok(items) => {
items.iter().map(|item_data| data::Item {
id: item_data.id,
name: item_data.name,
time_to_prepare: item_data.time_tp_prepare
}).collect()
},
Err(_) => Vec::new(),
}
},
Err(_) => Vec::new()
}
}
Looking at the example you linked your error is in not passing the Rc directly:
.query_map(rusqlite::params![vec![values]], |row| {
vs
.query_map([values], |row| {

Borrowing the mutable member used inside the loop

The problem I want to solve is:
Given the recursively nested data structure, eg. a JSON tree, and a path pointing to (possibly non-existent) element inside it, return the mutable reference of the element, that's the closest to given path.
Example: if we have JSON document in form { a: { b: { c: "foo" } } } and a path a.b.d, we want to have a mutable pointer to value stored under key "b".
This is a code snippet, what I've got so far:
use std::collections::HashMap;
enum Json {
Number(i64),
Bool(bool),
String(String),
Array(Vec<Json>),
Object(HashMap<String, Json>)
}
struct Pointer<'a, 'b> {
value: &'a mut Json,
path: Vec<&'b str>,
position: usize
}
/// Return a mutable pointer to JSON element having shared
/// the nearest common path with provided JSON.
fn nearest_mut<'a,'b>(obj: &'a mut Json, path: Vec<&'b str>) -> Pointer<'a,'b> {
let mut i = 0;
let mut current = obj;
for &key in path.iter() {
match current {
Json::Array(array) => {
match key.parse::<usize>() {
Ok(index) => {
match array.get_mut(index) {
Some(inner) => current = inner,
None => break,
}
},
_ => break,
}
} ,
Json::Object(map) => {
match map.get_mut(key) {
Some(inner) => current = inner,
None => break
}
},
_ => break,
};
i += 1;
}
Pointer { path, position: i, value: current }
}
The problem is that this doesn't pass through Rust's borrow checker, as current is borrowed as mutable reference twice, once inside match statement and once at the end of the function, when constructing the pointer method.
I've tried a different approaches, but not figured out how to achieve the goal (maybe going the unsafe path).
I completely misread your question and I owe you an apology.
You cannot do it in one pass - you're going to need to do a read-only pass to find the nearest path (or exact path), and then a read-write pass to actually extract the reference, or pass a mutator function in the form of a closure.
I've implemented the two-pass method for you. Do note that it is still pretty performant:
fn nearest_mut<'a, 'b>(obj: &'a mut Json, path: Vec<&'b str>) -> Pointer<'a, 'b> {
let valid_path = nearest_path(obj, path);
exact_mut(obj, valid_path).unwrap()
}
fn exact_mut<'a, 'b>(obj: &'a mut Json, path: Vec<&'b str>) -> Option<Pointer<'a, 'b>> {
let mut i = 0;
let mut target = obj;
for token in path.iter() {
i += 1;
// borrow checker gets confused about `target` being mutably borrowed too many times because of the loop
// this once-per-loop binding makes the scope clearer and circumvents the error
let target_once = target;
let target_opt = match *target_once {
Json::Object(ref mut map) => map.get_mut(*token),
Json::Array(ref mut list) => match token.parse::<usize>() {
Ok(t) => list.get_mut(t),
Err(_) => None,
},
_ => None,
};
if let Some(t) = target_opt {
target = t;
} else {
return None;
}
}
Some(Pointer {
path,
position: i,
value: target,
})
}
/// Return a mutable pointer to JSON element having shared
/// the nearest common path with provided JSON.
fn nearest_path<'a, 'b>(obj: &'a Json, path: Vec<&'b str>) -> Vec<&'b str> {
let mut i = 0;
let mut target = obj;
let mut valid_paths = vec![];
for token in path.iter() {
// borrow checker gets confused about `target` being mutably borrowed too many times because of the loop
// this once-per-loop binding makes the scope clearer and circumvents the error
let target_opt = match *target {
Json::Object(ref map) => map.get(*token),
Json::Array(ref list) => match token.parse::<usize>() {
Ok(t) => list.get(t),
Err(_) => None,
},
_ => None,
};
if let Some(t) = target_opt {
target = t;
valid_paths.push(*token)
} else {
return valid_paths;
}
}
return valid_paths
}
The principle is simple - I reused the method I wrote in my initial question in order to get the nearest valid path (or exact path).
From there, I feed that straight into the function that I had in my original answer, and since I am certain the path is valid (from the prior function call) I can safely unwrap() :-)

Using `pop3::POP3Stream::connect` to connect to runtime given `host`? [duplicate]

This question already has answers here:
How to convert a String into a &'static str
(4 answers)
Closed 7 years ago.
I'm trying to read input from the user, and then use it as the URL for the POP3 library. When converting the String that I get to a string slice, it doesn't live long enough to be used. This is strange to me for two reasons:
Because everything that uses the POP3 object is inside the same block, so the lifetime of the str slice should be that of the entire block, which would cover everything
I've tried almost every different code configuration I could think of, and to no avail, I get the same error every time.
extern crate pop3;
extern crate smtp;
extern crate openssl;
extern crate libc;
use openssl::ssl::{SslContext, SslMethod};
use pop3::POP3Stream;
use pop3::POP3Result::{POP3Stat, POP3List, POP3Message};
mod readline;
use readline::*;
fn main() {
let place = match readline("URL: ") { // Problem line
Some(input) => { // Problem line
let place: &'static str = &input[..]; // Problem line
let mut email_socket = match POP3Stream::connect(place, 995, Some(SslContext::new(SslMethod::Sslv23).unwrap())) { // Problem line
Ok(s) => s,
Err(e) => panic!("{}", e)
};
match readline("Username: ") {
Some(username) => {
match readline("Password: ") {
Some(password) => { email_socket.login(&*username, &*password); },
None => println!("Please enter a password.")
}
},
None => println!("Please enter a username.")
};
let stat = email_socket.stat();
match stat {
POP3Stat {num_email,
mailbox_size} => println!("num_email: {}, mailbox_size:{}", num_email, mailbox_size),
e => println!("There was an error signing into your server."),
}
let list_all = email_socket.list(None);
match list_all {
POP3List {emails_metadata} => {
for i in emails_metadata.iter() {
println!("message_id: {}, message_size: {}", i.message_id, i.message_size);
}
},
_ => println!("There was an error listing your messages."),
}
let message_25 = email_socket.retr(25);
match message_25 {
POP3Message{raw} => {
for i in raw.iter() {
println!("{}", i);
}
},
_ => println!("There was an error getting your 25th message."),
}
email_socket.quit();
},
None => { println!("Please enter a URL for your server."); }
};
}
The Problem
Your problem boils down to the use of static since the keyword basically says "keep this object around forever". This means that the lifetime of place, without a doubt, will live long after input — forever vs the scope of the block.
fn get() -> Option<String> {
Some("hello world".to_owned())
}
fn main() {
let data = match get() {
Some(input) => { let place : &'static str = &input[..]; },
None => { }
};
}
In the above we try to make place a static reference to a str, other other words; a reference that exists for the entire duration of our program. input on the other hand will definitely not exist for this amount of time, and therefor we get an error diagnostic.
<anon>:7:54: 7:59 error: `input` does not live long enough
<anon>:7 Some(input) => { let place : &'static str = &input[..]; },
The Solution
Remove the use of static, effectively saying that the lifetime of place is that of the block (which is a subset of the lifetime associated with input).
fn get() -> Option<String> {
Some("hello world".to_owned())
}
fn main() {
let data = match get() {
Some(input) => { let place : &str = &input[..]; },
None => { }
};
}
Further Digging
As it turns out, POP3Stream::connect accepts a &'static str as its first argument; this is really bad design since it will only accept string-literals.
impl Pop3Stream {
pub fn connect(host: &'static str, ...) -> Result<POP3Stream> {
...
}
}
https://github.com/mattnenterprise/rust-pop3/blob/master/src/pop3.rs
You can, however, hack your way around the issue by intentionally leaking the resource—effectively making it live "forever". Please note the usage of unsafe, and keep in mind that this is—by language design—considered to be just that.
fn get () -> Option<String> {
Some("hello world".to_owned ())
}
fn connect (host : &'static str) {
/* ... */
}
fn main() {
let data = match get() {
Some(input) => {
let place : &'static str = unsafe {
use std::mem; let x = mem::transmute(&input as &str);
mem::forget (x); x
};
connect(place);
},
None => { }
};
}

Resources