Hey so I'm really new to rust and programming at large and I'm having some issues I haven't encountered before. I'm attempting to return the results of user input variables to the screen outside of nesting but I haven't seemed to have any luck.
use std::{thread, time::Duration};
fn main(){
let mut modifer = 0;
let mut ModValue=0;
let mut ModReason = "";
let Y = "Y";
let y = "y";
let N = "N";
let n = "n";
print!("{esc}c", esc = 27 as char);
let mut ModYN = String::new();
println!("Are there any modifiers (Y/N)");
std::io::stdin().read_line(&mut ModYN).unwrap();
let ModYN = ModYN.trim();
if ModYN == y || ModYN == Y{
ModValue = 1;
print!("{esc}c", esc = 27 as char);
let mut modifer:f32=0.0;
let mut input = String::new();
//I'm attempting to get the input from the user to define a modifer reason and value
println!("Please enter the modifer: ");
std::io::stdin().read_line(&mut input).expect("Not a valid string");
modifer = input.trim().parse().expect("Not a valid number");
let mut ModReason = String::new();
println!("Whats the modifer reason: ");
std::io::stdin().read_line(&mut ModReason).unwrap();
let ModReason = ModReason.trim();
}
//Then print those results to the screen outside of nesting
print!("{esc}c", esc = 27 as char);
println!("-Modifer-");
println!("{}",&mut modifer);
println!("-Modifer Reason-");
println!("{}",&mut ModReason);
thread::sleep(Duration::from_secs(5));
}
I've attempted a variety of things; from assigning it as a mutable or borrowing the variable itself to even extending the nesting past the print results. But I've hit a brick wall and could really use some help.
There are several flaws in your code, and since your question in only about one, I'll explain only about that one. This means that the code below will not compile. But it will get you closer to the solution.
Your problem, I think, comes from the fact that you don't know the difference between let var = value and var = value. These are very different statements: the second one assigns value to var, while the first one creates a new variable (shadowing a variable with the same name, if there was one), then assigns to it. It is the same as
let var;
var = value;
So when, in the loop, you create a new variable ModReason, you are not modifying the one outside the loop: you are effectively creating a new variable, just to destroy it at the end of the if statement (without using it). So the solution is to not create a new variable each time:
use std::{thread, time::Duration};
fn main(){
let mut modifer = 0;
let mut ModValue=0;
let mut ModReason = "";
let Y = "Y";
let y = "y";
let N = "N";
let n = "n";
print!("{esc}c", esc = 27 as char);
let mut ModYN = String::new();
println!("Are there any modifiers (Y/N)");
std::io::stdin().read_line(&mut ModYN).unwrap();
let ModYN = ModYN.trim();
if ModYN == y || ModYN == Y{
ModValue = 1;
print!("{esc}c", esc = 27 as char);
let mut modifer:f32=0.0;
let mut input = String::new();
//I'm attempting to get the input from the user to define a modifer reason and value
println!("Please enter the modifer: ");
std::io::stdin().read_line(&mut input).expect("Not a valid string");
modifer = input.trim().parse().expect("Not a valid number");
ModReason = String::new();
println!("Whats the modifer reason: ");
std::io::stdin().read_line(&mut ModReason).unwrap();
ModReason = ModReason.trim();
}
//Then print those results to the screen outside of nesting
print!("{esc}c", esc = 27 as char);
println!("-Modifer-");
println!("{}",&mut modifer);
println!("-Modifer Reason-");
println!("{}",&mut ModReason);
thread::sleep(Duration::from_secs(5));
}
Rust would have warned you (because, in this way, you never assign to ModReason outside of the loop) if you didn't print ModReason by passing a mutable borrow (which is absolutely useless: just go with println!("{}", ModReason);).
Also, it is very likely that Rust has warned you about several variables that are not used: if you read these warnings, you can understand that you are doing something wrong.
Related
The code is to count the frequency of each word in an article. In the code, I implemented sequential, muti-thread, and muti-thread with a thread pool.
I test the running time of three methods, however, I found that the sequential method is the fastest one. I use the article (data) at 37423.txt, the code is at play.rust-lang.org.
Below is just the single- and multi version (without the threadpool version):
use std::collections::HashMap;
use std::sync::{Arc, Mutex};
use std::thread;
use std::time::SystemTime;
pub fn word_count(article: &str) -> HashMap<String, i64> {
let now1 = SystemTime::now();
let mut map = HashMap::new();
for word in article.split_whitespace() {
let count = map.entry(word.to_string()).or_insert(0);
*count += 1;
}
let after1 = SystemTime::now();
let d1 = after1.duration_since(now1);
println!("single: {:?}", d1.as_ref().unwrap());
map
}
fn word_count_thread(word_vec: Vec<String>, counts: &Arc<Mutex<HashMap<String, i64>>>) {
let mut p_count = HashMap::new();
for word in word_vec {
*p_count.entry(word).or_insert(0) += 1;
}
let mut counts = counts.lock().unwrap();
for (word, count) in p_count {
*counts.entry(word.to_string()).or_insert(0) += count;
}
}
pub fn mt_word_count(article: &str) -> HashMap<String, i64> {
let word_vec = article
.split_whitespace()
.map(|x| x.to_owned())
.collect::<Vec<String>>();
let count = Arc::new(Mutex::new(HashMap::new()));
let len = word_vec.len();
let q1 = len / 4;
let q2 = len / 2;
let q3 = q1 * 3;
let part1 = word_vec[..q1].to_vec();
let part2 = word_vec[q1..q2].to_vec();
let part3 = word_vec[q2..q3].to_vec();
let part4 = word_vec[q3..].to_vec();
let now2 = SystemTime::now();
let count1 = count.clone();
let count2 = count.clone();
let count3 = count.clone();
let count4 = count.clone();
let handle1 = thread::spawn(move || {
word_count_thread(part1, &count1);
});
let handle2 = thread::spawn(move || {
word_count_thread(part2, &count2);
});
let handle3 = thread::spawn(move || {
word_count_thread(part3, &count3);
});
let handle4 = thread::spawn(move || {
word_count_thread(part4, &count4);
});
handle1.join().unwrap();
handle2.join().unwrap();
handle3.join().unwrap();
handle4.join().unwrap();
let x = count.lock().unwrap().clone();
let after2 = SystemTime::now();
let d2 = after2.duration_since(now2);
println!("muti: {:?}", d2.as_ref().unwrap());
x
}
The result of mine is: single:7.93ms, muti: 15.78ms, threadpool: 25.33ms
I did the separation of the article before calculating time.
I want to know if the code has some problem.
First you may want to know the single-threaded version is mostly parsing whitespace (and I/O, but the file is small so it will be in the OS cache on the second run). At most ~20% of the runtime is the counting that you parallelized. Here is the cargo flamegraph of the single-threaded code:
In the multi-threaded version, it's a mess of thread creation, copying and hashmap overhead. To make sure it's not a "too little data" problem, I've used used 100x your input txt file and I'm measuring a 2x slowdown over the single-threaded version. According to the time command, it uses 2x CPU-time compared to wall-clock, so it seems to do some parallel work. The profile looks like this: (clickable svg version)
I'm not sure what to make of it exactly, but it's clear that memory management overhead has increased a lot. You seem to be copying strings for each hashmap, while an ideal wordcount would probably do zero string copying while counting.
More generally I think it's a bad idea to join the results in the threads - the way you do it (as opposed to a map-reduce pattern) the thread needs a global lock, so you could just pass the results back to the main thread instead for combining. I'm not sure if this is the main problem here, though.
Optimization
To avoid string copying, use HashMap<&str, i64> instead of HashMap<String, i64>. This requires some changes (lifetime annotations and thread::scope()). It makes mt_word_count() about 6x faster compared to the old version.
With a large input I'm measuring now a 4x speedup compared to word_count(). (Which is the best you can hope for with four threads.) The multi-threaded version is now also faster overall, but only by ~20% or so, for the reasons explained above. (Note that the single-threaded baseline has also profited the same &str optimization. Also, many things could still be improved/optimized, but I'll stop here.)
fn word_count_thread<'t>(word_vec: Vec<&'t str>, counts: &Arc<Mutex<HashMap<&'t str, i64>>>) {
let mut p_count = HashMap::new();
for word in word_vec {
*p_count.entry(word).or_insert(0) += 1;
}
let mut counts = counts.lock().unwrap();
for (word, count) in p_count {
*counts.entry(word).or_insert(0) += count;
}
}
pub fn mt_word_count<'t>(article: &'t str) -> HashMap<&'t str, i64> {
let word_vec = article.split_whitespace().collect::<Vec<&str>>();
// (skipping 16 unmodified lines)
let x = thread::scope(|scope| {
let handle1 = scope.spawn(move || {
word_count_thread(part1, &count1);
});
let handle2 = scope.spawn(move || {
word_count_thread(part2, &count2);
});
let handle3 = scope.spawn(move || {
word_count_thread(part3, &count3);
});
let handle4 = scope.spawn(move || {
word_count_thread(part4, &count4);
});
handle1.join().unwrap();
handle2.join().unwrap();
handle3.join().unwrap();
handle4.join().unwrap();
count.lock().unwrap().clone()
});
let after2 = SystemTime::now();
let d2 = after2.duration_since(now2);
println!("muti: {:?}", d2.as_ref().unwrap());
x
}
I am trying to make a decimal to hexidecimal converter in rust, and it works fine. However, it prints Some("") in front of the output like Some("1")Some("A")Some("4"). Does anyone know how to fix this? It could be the result of using String::from or I may need to parse the answer. Sorry if this is some easy fix as I am currently learning rust and I do not know all of the intricacies of rust. Thank you in advance!
main.rs here:
use std::{
io::{
self,
Write,
},
};
use std::collections::HashMap;
use std::process;
fn main() {
let mut hex_number_system = HashMap::new();
hex_number_system.insert(1,String::from("1"));
hex_number_system.insert(2,String::from("2"));
hex_number_system.insert(3,String::from("3"));
hex_number_system.insert(4,String::from("4"));
hex_number_system.insert(5,String::from("5"));
hex_number_system.insert(6,String::from("6"));
hex_number_system.insert(7,String::from("7"));
hex_number_system.insert(8,String::from("8"));
hex_number_system.insert(9,String::from("9"));
hex_number_system.insert(10,String::from("A"));
hex_number_system.insert(11,String::from("B"));
hex_number_system.insert(12,String::from("C"));
hex_number_system.insert(13,String::from("D"));
hex_number_system.insert(14,String::from("E"));
hex_number_system.insert(15,String::from("F"));
let mut line = 0;
let mut current_multiplier = 0;
let mut current_num = String::new();
let mut digit_num = 256;
print!("Enter a number from 0 - 4095:");
io::stdout().flush().unwrap();
let mut input = String::new();
io::stdin().read_line(&mut input).unwrap();
println!("{:?}", input);
let mut user = input.trim().parse::<i32>().unwrap();
if user > 4095 {
println!("Too high");
process::exit(1);
}
if user < 0 {
println!("Too low");
process::exit(1);
}
for i in 1..=3 {
current_multiplier = 15;
loop {
if current_multiplier == 0 {
print!("{}", 0);
digit_num /= 16;
break;
}
if user >= (current_multiplier * digit_num) {
print!("{:?}", hex_number_system.get(¤t_multiplier));
user -= &digit_num * ¤t_multiplier;
digit_num /= 16;
break;
} else {
current_multiplier -= 1;
}
}
}
print!("\n");
}
If you look at the doc of the HashMap::get function, you'll see that it returns an Option. The doc doesn't say, but that's to handle the case when the key is not found in the map (the example in the doc shows this).
You have to handle this possibility. If you don't mind your code crashing, you can just .unwrap() the result. Otherwise, match it properly.
This program takes as input a sentence " Add name to dept", passes the string into a function that splits the string by whitespace into a vector that then is inserted into a hashmap that is suppose to retain the values of name and dept, which it does for 1 input. On the second input only the first word "Add" is printed. Are there any glaring missteps that may cause this odd output?
use std::io;
use std::collections::HashMap;
fn main() {
let mut input = String::new();
let mut db: HashMap<String,String> = HashMap::new();
loop {
println!("'Add <name> to <department>'");
io::stdin().read_line(&mut input).expect("Not input");
add_to_hashmap(&input, &mut db);
}
}
fn add_to_hashmap(input: &String, db: &mut HashMap<String,String>){
let v: Vec<&str> = input.split(" ").collect();
db.insert(v[1].to_string(),v[3].to_string());
for (name, dept) in db{
println!("{}, {}", name, dept);
}
}
Investigation
To diagnose this I added a dbg! call to check the value of input each time add_to_hashmap is called.
let v: Vec<&str> = dbg!(input).split(" ").collect();
The first time it prints:
'Add <name> to <department>'
Add john to math
[src/main.rs:13] input = "Add john to math\n"
john, math
The second time:
'Add <name> to <department>'
Add bill to science
[src/main.rs:13] input = "Add john to math\nAdd bill to science\n"
john, math
Diagnosis
input isn't being cleared. read_line doesn't erase the input buffer; it just appends to it.
In the documentation you can see that the example code clears the buffer after each call:
use std::io::{self, BufRead};
let mut cursor = io::Cursor::new(b"foo\nbar");
let mut buf = String::new();
// cursor is at 'f'
let num_bytes = cursor.read_line(&mut buf)
.expect("reading from cursor won't fail");
assert_eq!(num_bytes, 4);
assert_eq!(buf, "foo\n");
buf.clear();
// cursor is at 'b'
let num_bytes = cursor.read_line(&mut buf)
.expect("reading from cursor won't fail");
assert_eq!(num_bytes, 3);
assert_eq!(buf, "bar");
buf.clear();
// cursor is at EOF
let num_bytes = cursor.read_line(&mut buf)
.expect("reading from cursor won't fail");
assert_eq!(num_bytes, 0);
assert_eq!(buf, "");
I'm pretty new to Rust (and bio rust) and just learning so any guidance would be appreciated. I've read the small Read Example for how to read in a fasta sequence from stdin,
let mut records = fasta::Reader::new(io::stdin()).records();
but I can't figure out how to read in from a file. I've tried
let mut records = fasta::Reader::new(filename);
Where the filename is a slice and a string and I've found the from_file function trying that as well. While some of them appear to work, then when I try to parse through them with for or while loops, they always complain that they're of the wrong type. The from_file function seems to not make an iterator, but a Result reader, so I can't call the next() or collect() function on it,
let mut records = fasta::Reader::from_file(filename);
let mut nb_reads = 0;
let mut nb_bases = 0;
while let Some(Ok(record)) = records.next() {
nb_reads += 1;
nb_bases += record.seq().len();
let sa = suffix_array(record.seq());
println!("Here's the Suffix array: {:#?}", sa);
nb_reads += 1;
nb_bases += record.seq().len();
}
while the for loop seems to work, but the 'result' iterator doesn't have the right type so I can't pull sequences.
let mut reader = fasta::Reader::from_file(filename);
let mut nb_reads = 0;
let mut nb_bases = 0;
for result in reader {
nb_reads += 1;
nb_bases += result.seq().len();
let sa = suffix_array(result.seq());
println!("Here's the Suffix array: {:#?}", sa);
nb_reads += 1;
nb_bases += result.seq().len();
}
I'm stumped, but I feel like I'm close to getting it to work. Thanks!
I found something that works, although I'm not sure if it's an answer
fn main() {
let args: Vec<String> = env::args().collect();
let filename: &str = &args[1];
let reader = fasta::Reader::from_file(filename).unwrap();
let mut nb_reads = 0;
let mut nb_bases = 0;
for result in reader.records() {
let result_data = &result.unwrap();
nb_reads += 1;
nb_bases += result_data.seq().len();
println!("{:?}",result_data.id());
}
println!("Number of reads: {}", nb_reads);
println!("Number of bases: {}", nb_bases);
}
It appears that you need to get the pointer of the unwrapped records in the fasta reader. I wish the documentation was a bit more helpful for beginners, but I'll leave this here for anyone else struggling. Also if you try to print the .seq() it may crash your terminal haha. Moral of the story is check your types and deal with Result types (I'll figure out how to properly unwrap them later, I know my code is far from optimal now)
You need to handle the Result in order to get the type you expect.
For example:
let reader = fasta::Reader::from_file(filename).expect("Unable to open");
See this thread for more details:
Unable to read file contents to string - Result does not implement any method in scope named `read_to_string`
I'm trying to implement a simple parser for a byte stream.
I'm having troubles when I want to reuse a variable I declared previously,
fn read_data(asn_data: &mut Cursor<&[u8]>) -> Result<(u8, u8, Vec<u8>), Err> {
let total_len = asn_data.get_ref().len();
if total_len < 2 {
return Err(1);
}
let d_type = asn_data.read_u8().unwrap();
let d_len = asn_data.read_u8().unwrap();
if (asn_data.position() + d_len as u64) > total_len as u64 {
return Err(2);
}
let mut buf = vec![0; d_len as usize];
match asn_data.read_exact(&mut buf) {
Err(e) => Err(e),
Ok(()) => Ok((d_type, d_len, buf)),
}
}
fn parse_request(request: &[u8]) -> Option<u8> {
if request.len() == 0 {
return None;
}
let mut rdr = Cursor::new(request);
let data_tuple = read_data(&mut rdr).unwrap();
println!("{:02?}", data_tuple.2);
rdr = Cursor::new(data_tuple.2.as_slice());
let data_tuple = read_data(&mut rdr).unwrap();
println!("{:02x?}", data_tuple.2);
Some(1)
}
In the parse_request function I want to reuse rdr variable, but with the code shown above I get the next error when compiling:
error[E0597]: data_tuple.2 does not live long enough -->
src/main.rs:80:23
| 80 | rdr = Cursor::new(data_tuple.2.as_slice());
| ^^^^^^^^^^^^ borrowed value does not live long enough ... 104 | }
| - data_tuple.2 dropped here while still borrowed
|
= note: values in a scope are dropped in the opposite order they are created
error: aborting due to previous error
However if I write "let mut" when I use the 2nd time rdr variable, the code compiles and works fine...
let mut rdr = Cursor::new(data_tuple.2.as_slice());
I don't understand why... what I want is to reuse the variable instead to declare it again...
I tried with some examples/issues related to variable life time but I didn't get the solution for my case... and the solution I found I don't understand fully...
This is not connected with tuple lifetimes, this is just the drop order.
When the variables are defined in separate let statements in the same scope (that is, in the same block), they will be dropped in reverse order. Looking at your code, we can see:
let mut rdr = Cursor::new(request);
let data_tuple = read_data(&mut rdr).unwrap();
So, data_tuple will be dropped first, while rdr is still alive. This is bad, because rdr must reference the tuple. The easiest fix will be to swap their definitions:
let data_tuple: (u8, u8, Vec<u8>);
let mut rdr = Cursor::new(request);
data_tuple = read_data(&mut rdr).unwrap();
This way, rdr will be dropped first, releasing the reference to data_tuple and letting the tuple be dropped itself.
The "fix" you mentioned works, because every let statement defines new variable, even if the same name is already used, and the existing variable is immediately forgotten. So, when you write:
let mut rdr = Cursor::new(request);
let data_tuple = read_data(&mut rdr).unwrap();
let mut rdr = Cursor::new(data_tuple.2.as_slice());
the second rdr is in no way connected with the first. Essentially, it's almost the same as declaring two different variables, say, rdr and rdr2, and using rdr2 from this place until the end of function.