This question already has an answer here:
Why is my code failling in Rust when I don't declare a variable in the loop
(1 answer)
Closed 5 months ago.
I've just finished the 4th chapter of 'The Book'. To test my knowledge I was building this little project. But, for some reason I/O is not working the way I'm expecting. I was trying to figure out problem of my code, but could not. I'd really appriciate if someone can tell what the problem is.
here's the code:
use std::{
io::{stdin, stdout, Write},
vec,
};
fn main() {
let mut db: Vec<String> = vec![];
let mut key = String::new();
let mut value = String::new();
let mut operation = String::new();
let operations = (
String::from("put"),
String::from("get"),
String::from("exit"),
);
println!(
"\n\tyour options are {}, {}, {}.\n",
operations.0, operations.1, operations.2
);
loop {
println!("what do you wanna do?");
stdin()
.read_line(&mut operation)
.expect("colud not parse input");
let operation = operation.trim();
if operation == operations.0 {
println!("enter your key");
stdin()
.read_line(&mut key)
.expect("could not parse the input");
let key = key.trim();
println!("enter your value");
stdin()
.read_line(&mut value)
.expect("could not parse the input");
let value = value.trim();
store(&mut db, &key, &value);
println!("\n\tvalue `{1}` stored to key `{0}`\n", key, value);
} else if operation == operations.1 {
println!("enter your key");
stdin()
.read_line(&mut key)
.expect("could not parse the input");
let key = key.trim();
println!("\n\tthe value of {} is {}\n", key, get(&mut db, &key));
} else if operation == operations.2 {
break;
} else {
println!("\n\t{} is not a valid option\n", operation);
}
stdout().flush().unwrap();
}
}
fn store(db: &mut Vec<String>, key: &str, value: &str) {
let new_data = format!("{},{}", key, value);
db.push(new_data);
}
fn get(db: &mut Vec<String>, key: &str) -> String {
for data in db {
if data.split(',').next().expect("error while parsing the key") == key {
return data
.split(',')
.next()
.expect("error while parsing the value")
.to_string();
}
}
String::from("404")
}
output:
your options are put, get, exit.
what do you wanna do?
put
enter your key
name
enter your value
cob
value `cob` stored to key `name`
what do you wanna do?
exit
put
exit is not a valid option
what do you wanna do?
exit
put
exit
exit is not a valid option
You need to look at the documentation of what read_line actually does:
Locks this handle and reads a line of input, appending it to the specified buffer.
Meaning, it doesn't replace the content of operation, it appends to it.
There are two possible easy fixes:
clear operation before reading
don't keep operation between iterations
I'd go with #2, as I think there is no reason why operation is defined outside of the loop.
use std::{
io::{stdin, stdout, Write},
vec,
};
fn main() {
let mut db: Vec<String> = vec![];
let mut key = String::new();
let mut value = String::new();
let operations = (
String::from("put"),
String::from("get"),
String::from("exit"),
);
println!(
"\n\tyour options are {}, {}, {}.\n",
operations.0, operations.1, operations.2
);
loop {
println!("what do you wanna do?");
let mut operation = String::new();
stdin()
.read_line(&mut operation)
.expect("colud not parse input");
let operation = operation.trim();
if operation == operations.0 {
println!("enter your key");
stdin()
.read_line(&mut key)
.expect("could not parse the input");
let key = key.trim();
println!("enter your value");
stdin()
.read_line(&mut value)
.expect("could not parse the input");
let value = value.trim();
store(&mut db, &key, &value);
println!("\n\tvalue `{1}` stored to key `{0}`\n", key, value);
} else if operation == operations.1 {
println!("enter your key");
stdin()
.read_line(&mut key)
.expect("could not parse the input");
let key = key.trim();
println!("\n\tthe value of {} is {}\n", key, get(&mut db, &key));
} else if operation == operations.2 {
break;
} else {
println!("\n\t{} is not a valid option\n", operation);
}
stdout().flush().unwrap();
}
}
fn store(db: &mut Vec<String>, key: &str, value: &str) {
let new_data = format!("{},{}", key, value);
db.push(new_data);
}
fn get(db: &mut Vec<String>, key: &str) -> String {
for data in db {
if data.split(',').next().expect("error while parsing the key") == key {
return data
.split(',')
.next()
.expect("error while parsing the value")
.to_string();
}
}
String::from("404")
}
Related
I am new to Rust and I am trying to write a simple program that encrypts files with AES once the user provides a Path and a password. The code creates as many threads as the number of virtual CPUs, and encrypts every file in a particular path in a new file with the same name + .encryption at the end.
But for some peculiar reason I haven't yet understood, the last lines of the program are never reached even though there are no errors or warnings. The code looks like this:
fn encrypt_file(
filepath: &str,
pass: &str,
) -> Result<(), io::Error> {
println!("The filepath: {}", filepath);
let text_vec = String::from_utf8_lossy(&fs::read(filepath).unwrap()).as_bytes().to_vec();
println!("File read complete:");
let mut encryptionPassword: String;
if pass.len() < 32 {
let length = 32 - pass.len();
let strr = (0..length).map(|_| "0").collect::<String>().to_owned();
encryptionPassword = format!("{}{}", pass, strr);
} else if pass.len() > 32 {
encryptionPassword = String::from(&pass[..32]);
} else {
encryptionPassword = String::from(pass);
}
println!("encryptionPassword: {:?}",&encryptionPassword);
let ciphertext = enc_file::encrypt_aes(text_vec, &encryptionPassword.as_str()).unwrap();
let enc = ".encrypted";
let dist = format!("{}{}", filepath, enc);
fs::write(&dist, &ciphertext)?;
println!("wrote");
let mut buffer = String::new();
File::open(&dist).unwrap().read_to_string(&mut buffer)?;
println!("file opened");
let decrypted = enc_file::decrypt_aes(buffer.into_bytes().to_vec(), encryptionPassword.as_str());
println!("Decrypted: {:?}",decrypted );
Ok(())
}
fn main() {
let matches = Command::new("Multithreaded encryptor")
.version("0.1.0")
.arg(Arg::new("password")
.short("p".parse().unwrap())
.long("password")
.takes_value(true)
.help("Encryption password"))
.arg(Arg::new("path")
.short("D".parse().unwrap())
.long("path") //Double quotes needed!
.takes_value(true)
.help("Path to files"))
.get_matches();
let pass = matches.value_of("password").unwrap_or_else(
|| { "null" }
);
let path = matches.value_of("path").unwrap_or_else(
|| { process::exit(1); }
);
println!("The password: {}", pass);
println!("The path: {}", path);
let thread_pool = ThreadPool::new(num_cpus::get()); //As many threads as virtual CPUs.
for entry in WalkDir::new(path)
.into_iter()
.filter_map(|e| e.ok())
.filter(|e| !e.path().is_dir())
{
let filepath = entry.path().to_owned();
let a3 = pass.to_owned();
thread_pool.execute(move || {
encrypt_file(filepath.to_str().unwrap(), a3.as_str()).unwrap();
});
}
}
The " println!("file opened");" line and everything below, hasn't been reached a single time so far, yet there are no errors or warnings. The .encrypted file is successfully created and
includes encrypted data, but nothing below it seems to be executed.
There also seems to be a degree of randomness, since sometimes the encrypted file contains no data, but this could be a peculiarity of the underlying file-system...
In Rust, the program will terminate if the main() function finishes, regardless of whether any other threads are alive.
You need to wait for the outstanding jobs submitted to the pool to finish as the last step in main(). You can do this with ThreadPool::join():
thread_pool.join();
I have a rust program that creates temporary email addresses using the mail.tm API, and I want to use threads to create emails simultaneously, to increase the speed. However, what I have tried, only results in printing "Getting email.." x amount of times, and exiting. I am unsure what to do about this. Any help or suggestions are appreciated.
use json;
use rand::distributions::Alphanumeric;
use rand::{thread_rng, Rng};
use reqwest;
use reqwest::header::{HeaderMap, HeaderValue, ACCEPT, CONTENT_TYPE};
use std::{collections::HashMap, io, iter, vec::Vec};
use std::thread;
fn gen_address() -> Vec<String> {
let mut rng = thread_rng();
let address: String = iter::repeat(())
.map(|()| rng.sample(Alphanumeric))
.map(char::from)
.take(10)
.collect();
let password: String = iter::repeat(())
.map(|()| rng.sample(Alphanumeric))
.map(char::from)
.take(5)
.collect();
let body = reqwest::blocking::get("https://api.mail.tm/domains")
.unwrap()
.text()
.unwrap();
let domains = json::parse(&body).expect("Failed to parse domain json.");
let domain = domains["hydra:member"][0]["domain"].to_string();
let email = format!("{}#{}", &address, &domain);
vec![email, password]
}
fn gen_email() -> Vec<String> {
let client = reqwest::blocking::Client::new();
let address_info = gen_address();
let address = &address_info[0];
let password = &address_info[1];
let mut data = HashMap::new();
data.insert("address", &address);
data.insert("password", &password);
let mut headers = HeaderMap::new();
headers.insert(ACCEPT, HeaderValue::from_static("application/ld+json"));
headers.insert(
CONTENT_TYPE,
HeaderValue::from_static("application/ld+json"),
);
let res = client
.post("https://api.mail.tm/accounts")
.headers(headers)
.json(&data)
.send()
.unwrap();
vec![
res.status().to_string(),
address.to_string(),
password.to_string(),
]
}
fn main() {
fn get_amount() -> i32 {
let mut amount = String::new();
loop {
println!("How many emails do you want?");
io::stdin()
.read_line(&mut amount)
.expect("Failed to read line.");
let _amount: i32 = match amount.trim().parse() {
Ok(num) => return num,
Err(_) => {
println!("Please enter a number.");
continue;
}
};
}
}
let amount = get_amount();
let handle = thread::spawn(move || {
for _gen in 0..amount {
let handle = thread::spawn(|| {
println!("Getting email...");
let maildata = gen_email();
println!(
"Status: {}, Address: {}, Password: {}",
maildata[0], maildata[1], maildata[2]);
});
}
});
handle.join().unwrap();
}
Rust Playground example
I see a number of sub-threads being spawned from an outer thread. I think you might want to keep those handles and join them. Unless you join those sub threads the outer thread will exit early. I set up a Rust Playground to demonstrate ^^.
In the playground example, first run the code as-is and note the output of the code - the function it's running is not_joining_subthreads(). Note that it terminates rather abruptly. Then modify the code to call joining_subthreads(). You should then see the subthreads printing out their stdout messages.
let handle = thread::spawn(move || {
let mut handles = vec![];
for _gen in 0..amount {
let handle = thread::spawn(|| {
println!("Getting email...");
let maildata = gen_email();
println!(
"Status: {}, Address: {}, Password: {}",
maildata[0], maildata[1], maildata[2]);
});
handles.push(handle);
}
handles.into_iter().for_each(|h| h.join().unwrap());
});
handle.join().unwrap();
I'd like to find a keyword in a text file or in a variable. I want to get user input and paste it into a text file.
my code:
use std::fs;
fn main() {
let mut user_name = String::new();
println!("Hello what is your name: ");
std::io::stdin().read_line(&mut name).unwrap();
fs::write("files/user_name.txt", user_name).unwrap();
let mut user_wish = String::new();
println!("What is your wish: ");
std::io::stdin().read_line(&mut user_wish).unwrap();
fs::write("files/user_wish.txt", user_wish).unwrap();
}
I don't know how to find keywords like my and name in the text file user_wish.txt so that I can list user_name.
use std::fs;
static KEYWORDS: &'static [&'static str] = &["my", "name"];
fn main() {
let mut user_name = String::new();
println!("Hello what is your name: ");
std::io::stdin().read_line(&mut user_name).unwrap();
// ---
// fs::write<P, C>(path: P, contents: C)
// -- Note contents takes ownership of `C` / `user_clone`
// that memory will be consumed after it finishes
// so we give it clone(), so we can still access `user_name`
fs::write("files/user_name.txt", user_name.clone()).unwrap();
for kword in KEYWORDS {
if user_name.contains(kword) {
println!("contains keyword");
}
}
let mut user_wish = String::new();
println!("What is your wish: ");
std::io::stdin().read_line(&mut user_wish).unwrap();
fs::write("files/user_wish.txt", user_wish).unwrap();
}
I want to implement a user interaction script in the form of a lightweight, long-lived thread written in Rust. Inside the script, I have points where I asynchronously await user input.
In JavaScript, I would use a generator, inside which you can pass a question, and get back an answer, for example:
function* my_scenario() {
yield "What is your name?";
let my_name = yield "How are you feeling?";
let my_mood = yield "";
...
}
let my_session = my_scenario();
...
my_session.next("Peter");
my_session.next("happy");
However, Rust's generator method resume() contains no parameters! I cannot clone a generator or return it from a function in order to have many user sessions with different states. Instead of a generator, I thought of using an async fn(), but I do not understand how to call it at each step, passing the value there.
The return value from yield is effectively just another generator that has been implicitly passed to the first generator, except that it forces the two to be tied together in weird ways.
You can see that in your original code by the junk yield "" that you need in order to get a value even though you don't have anything to return. Additionally, your example requires that the user of the generator know the answer to the question before it is asked, which seems very unorthodox.
Explicitly pass in a second generator:
#![feature(generators, generator_trait)]
use std::{
io,
ops::{Generator, GeneratorState},
};
fn user_input() -> impl Generator<Yield = String> {
|| {
let input = io::stdin();
loop {
let mut line = String::new();
input.read_line(&mut line).unwrap();
yield line;
}
}
}
fn my_scenario(
input: impl Generator<Yield = String>,
) -> impl Generator<Yield = &'static str, Return = String> {
|| {
let mut input = Box::pin(input);
yield "What is your name?";
let my_name = match input.as_mut().resume(()) {
GeneratorState::Yielded(v) => v,
GeneratorState::Complete(_) => panic!("input did not return a value"),
};
yield "How are you feeling?";
let my_mood = match input.as_mut().resume(()) {
GeneratorState::Yielded(v) => v,
GeneratorState::Complete(_) => panic!("input did not return a value"),
};
format!("{} is {}", my_name.trim(), my_mood.trim())
}
}
fn main() {
let my_session = my_scenario(user_input());
let mut my_session = Box::pin(my_session);
loop {
match my_session.as_mut().resume(()) {
GeneratorState::Yielded(prompt) => {
println!("{}", prompt);
}
GeneratorState::Complete(v) => {
println!("{}", v);
break;
}
}
}
}
$ cargo run
What is your name?
Shep
How are you feeling?
OK
Shep is OK
You can provide hard-coded data as well:
let user_input = || {
yield "Peter".to_string();
yield "happy".to_string();
};
let my_session = my_scenario(user_input);
As of approximately Rust nightly 2020-02-08, Rust's generators now accept an argument to resume, more closely matching the original JavaScript example:
#![feature(generators, generator_trait)]
use std::{
io::{self, BufRead},
ops::{Generator, GeneratorState},
};
fn my_scenario() -> impl Generator<String, Yield = &'static str, Return = String> {
|_arg: String| {
let my_name = yield "What is your name?";
let my_mood = yield "How are you feeling?";
format!("{} is {}", my_name.trim(), my_mood.trim())
}
}
fn main() {
let my_session = my_scenario();
let mut my_session = Box::pin(my_session);
let stdin = io::stdin();
let mut lines = stdin.lock().lines();
let mut line = String::new();
loop {
match my_session.as_mut().resume(line) {
GeneratorState::Yielded(prompt) => {
println!("{}", prompt);
}
GeneratorState::Complete(v) => {
println!("{}", v);
break;
}
}
line = lines.next().expect("User input ended").expect("User input malformed");
}
}
With the following code, I tried to return &str of temperature of user input, but in vain. Then, I am trying to return f32, but still struggle...
Q1. The reason I am getting the error at the bottom is because the scope of 'let temp = String::new();' still persists, even I 'shadow' it later by 'let temp = temp.trim().parse::<f32>();' within the loop?
Q2. How can I rewrite the code so that it returns &str?
fn gettemp() -> f32 {
let temp = String::new();
loop {
println!("What is your temperature?");
io::stdin().read_line(&mut temp).expect("Failed to read the line");
let temp = temp.trim().parse::<f32>();
if !temp.is_ok() {
println!("Not a number!");
} else {
break;
}
}
temp
}
Error:
error[E0308]: mismatched types
--> src/main.rs:70:5
|
49 | fn gettemp() -> f32 {
| --- expected `f32` because of return type
...
70 | temp
| ^^^^ expected f32, found struct `std::string::String`
|
= note: expected type `f32`
found type `std::string::String`
A1 - nope, that's not how shadowing works. Let's look at your code with comments.
fn gettemp() -> f32 {
let temp = String::new(); // Outer
loop {
// There's no inner temp at this point, even in the second
// loop pass, etc.
println!("What is your temperature?");
// Here temp refers to the outer one (outside of the loop)
io::stdin().read_line(&mut temp).expect("Failed to read the line");
// Shadowed temp = let's call it inner temp
let temp = temp.trim().parse::<f32>();
// ^ ^
// | |- Outer temp
// |- New inner temp
// temp refers to inner temp
if !temp.is_ok() {
println!("Not a number!");
} else {
// Inner temp goes out of scope
break;
}
// Inner temp goes out of scope
}
// Here temp refers to outer one (String)
temp
}
A2 - you can't return &str. #E_net4 posted a link to the answer why. However, you can return String. You can do something like this nn case you'd like to have a validated String:
fn gettemp() -> String {
loop {
println!("What is your temperature?");
let mut temp = String::new();
io::stdin()
.read_line(&mut temp)
.expect("Failed to read the line");
let trimmed = temp.trim();
match trimmed.parse::<f32>() {
Ok(_) => return trimmed.to_string(),
Err(_) => println!("Not a number!"),
};
}
}
I see couple of another problems in your code.
let temp = String::new();
Should be let mut temp, because you'd like to borrow mutable reference later (&mut temp in the read_line call).
Another issue is the loop & read_line. read_line appends to the String. Run this code ...
let mut temp = "foo".to_string();
io::stdin().read_line(&mut temp).unwrap();
println!("->{}<-", temp);
... and enter 10 for example. You'll see following output ...
->foo10
<-
... which is not what you want. I'd rewrite gettemp() in this way:
fn gettemp() -> f32 {
loop {
println!("What is your temperature?");
let mut temp = String::new();
io::stdin()
.read_line(&mut temp)
.expect("Failed to read the line");
match temp.trim().parse() {
Ok(temp) => return temp,
Err(_) => println!("Not a number!"),
};
}
}
IMHO explicit return temp is much cleaner & readable (compared to suggested break out of the loop with a value).
A3 - Why we don't need to explicitly state <f32> in temp.trim().parse()
It's inferred by the compiler.
fn gettemp() -> f32 { // 1. f32 is return type
loop {
println!("What is your temperature?");
let mut temp = String::new();
io::stdin()
.read_line(&mut temp)
.expect("Failed to read the line");
match temp.trim().parse() {
// 4. parse signature is pub fn parse<F>(&self) -> Result<F, ...>
// compiler knows it must be Result<f32, ...>
// Result<f32, ...> = Result<F, ...> => F = f32
// F was inferred and there's no need to explicitly state it
Ok(temp) => return temp,
// | |
// | 2. return type is f32, temp must be f32
// |
// | 3. temp must be f32, the parse result must be Result<f32, ...>
Err(_) => println!("Not a number!"),
};
}
}
Regarding question 1, you can break out of the loop with a value:
fn gettemp() -> f32 {
let mut temp = String::new();
loop {
println!("What is your temperature?");
io::stdin().read_line(&mut temp).expect("Failed to read the line");
let temp = temp.trim().parse::<f32>();
if !temp.is_ok() {
println!("Not a number!");
} else {
break temp.unwrap() // yield value when breaking out of loop
}
}
}
This way, the whole loop's value is the thing you passed along with break.
Regarding question 2, I am not sure if you really want to do this, because &str is a borrowed type. I think you want to return an String in this case which owns the data.
In your program, loop { ... } creates a new scope. The scope of the second temp starts where it's defined and ends when loop ends. See the following example:
fn main() {
let a = 1;
{
let a = 2;
println!("{}", a);
}
println!("{}", a);
}
This prints 2, 1。
If you want to return a string, use (the code is fixed according to the comment below):
fn gettemp() -> String {
loop {
let mut temp = String::new();
println!("What is your temperature?");
std::io::stdin().read_line(&mut temp).expect("Failed to read the line");
temp = temp.trim().to_string();
match temp.parse::<f32>() {
Err(_) => println!("Not a number!"),
_ => return temp,
}
}
}
&str is a borrowed reference. You cannot return a borrowed reference to a local variable which will be released when the function returns.