Can't borrow data as a '&' reference as mutable - rust

I have this function that looks like this
Install Magic-Crypt to reproduce the error.
magic-crypt = "3.1.10"
Import the following: pub use magic_crypt::{new_magic_crypt, MagicCryptTrait, MagicCryptError};
Here is the function
pub fn magic_decrypt(mut phrase: &String, hash: &String, decrypt: String) -> Option<Result<String, MagicCryptError>> {
println!("Please insert your decryption phrase: ");
std::io::stdin().read_line(&mut phrase).expect("Failed to read line");
if &hash_string(&phrase.replace("\r", "").clone().to_string()) == hash {
println!("Success, you've entered the correct phrase, decrypting...");
return Some(new_magic_crypt!(phrase.replace("\r", "").clone().to_string(), 256).decrypt_base64_to_string(&decrypt));
} else {
println!("Failed to enter the correct phrase, please try again.");
while &hash_string(&phrase.replace("\r", "").clone().to_string()) != hash {
println!("Please insert your decryption phrase: ");
std::io::stdin().read_line(&mut phrase).expect("Failed to read line");
let _phrase = phrase.replace("\r", "").clone().to_string();
}
return None;
}
}
The problem is that I get these errors (despite me setting the variable as mutable)
error[E0596]: cannot borrow data in a `&` reference as mutable
--> crypto/crypto.rs:78:40
|
78 | std::io::stdin().read_line(&mut phrase).expect("Failed to read line");
| ^^^^^^^^^^^ cannot borrow as mutable
What can I do to fix this? If this question is bad, please tell me as well, thanks!

mut phrase: &String means that the phrase variable is a mutable object that references an immutable string. What you probably tried to do is phrase: &mut String, which is an immutable variable that references a mutable string.
You can then also remove the &mut reborrow at the read_line function call.
pub fn magic_decrypt(phrase: &mut String, hash: &String, decrypt: String) -> Option<Result<String, MagicCryptError>> {
println!("Please insert your decryption phrase: ");
std::io::stdin().read_line(phrase).expect("Failed to read line");
if &hash_string(&phrase.replace("\r", "").clone().to_string()) == hash {
println!("Success, you've entered the correct phrase, decrypting...");
return Some(new_magic_crypt!(phrase.replace("\r", "").clone().to_string(), 256).decrypt_base64_to_string(&decrypt));
} else {
println!("Failed to enter the correct phrase, please try again.");
while &hash_string(&phrase.replace("\r", "").clone().to_string()) != hash {
println!("Please insert your decryption phrase: ");
std::io::stdin().read_line(phrase).expect("Failed to read line");
let _phrase = phrase.replace("\r", "").clone().to_string();
}
return None;
}
}
Further problems I detected:
.replace() already returns a String, .clone() on it creates the same String again, and to_string() yet again creates the same String. Just leave out the entire .clone().to_string().
use &str instead of &String in your arguments as it is a strict superset of it. This also eliminates all the & borrows you use to compare String with &String, as String is directly comparable with &str.
the let _phrase = phrase.replace(...) does nothing.
return statements in Rust at the end of a {} are not necessary, you can directly write None instead of return None;.
if you replace a single character, it is recommended to specify it as a char instead of a string ('\r' instead of "\r").
You always return None if you enter the else case, even if a correct phrase was entered. This one probably requires structural changes in your if/while constructs.
Your code isn't formatted with cargo fmt. I recommend enabling auto-format.
Some of those fixes come from cargo clippy, which I can recommend to run from time to time as it gives valuable insights for improving your code quality.
Here I fixed some of those.
pub fn magic_decrypt(
phrase: &mut String,
hash: &str,
decrypt: &str,
) -> Option<Result<String, MagicCryptError>> {
println!("Please insert your decryption phrase: ");
std::io::stdin()
.read_line(phrase)
.expect("Failed to read line");
if hash_string(&phrase.replace('\r', "")) == hash {
println!("Success, you've entered the correct phrase, decrypting...");
Some(new_magic_crypt!(phrase.replace('\r', ""), 256).decrypt_base64_to_string(decrypt))
} else {
println!("Failed to enter the correct phrase, please try again.");
while hash_string(&phrase.replace('\r', "")) != hash {
println!("Please insert your decryption phrase: ");
std::io::stdin()
.read_line(phrase)
.expect("Failed to read line");
let _phrase = phrase.replace('\r', "");
}
None
}
}
The last point I mentioned, that you always return None in your else case, seems to indicate that your if/while layout is incorrect.
This is just a wild guess, but I assume you attempted to write something like this:
pub fn magic_decrypt(
phrase: &mut String,
hash: &str,
decrypt: &str,
) -> Result<String, MagicCryptError> {
println!("Please insert your decryption phrase: ");
loop {
std::io::stdin()
.read_line(phrase)
.expect("Failed to read line");
*phrase = phrase.trim_end().to_string();
if hash_string(phrase) == hash {
println!("Success, you've entered the correct phrase, decrypting...");
break new_magic_crypt!(hash, 256).decrypt_base64_to_string(decrypt);
} else {
println!("Failed to enter the correct phrase, please try again.");
}
}
}
A last point I would like to mention because it's quite important: If you use hashing for storing passwords, it's crucial that you use salting.

Related

I keep getting the following error thread 'main' panicked at 'Jjaanokapasao: ParseIntError { kind: InvalidDigit }', fahrenheit_celsius.rs:17:49

I'm very newbie when it comes related to Rust, and I keep getting this error and honestly, I have no clue what's going on. I'm doing a Fahrenheit to Celsius, and the other way around.
Here is my code:
use std::io;
fn main() {
let mut choose = String::new();
println!("Choose between Celsius To Fahrenheit [1] or Fahrenheit to Celsius [2],\
please introduce a corrected value (integer)");
io::stdin()
.read_line(&mut choose)
.expect("Failed to read!");
// Careful with the trim, cuz it's removing all characters and it causes and error that basically, closes the CMD
// Carriage return BE CAREFUL MAN!
if choose.trim() == "1" {
println!("Please, Introduce a value ");
io::stdin().read_line(&mut choose).expect("Please, enter an integer");
let choose: i32 = choose.trim().parse().expect("Jjaanokapasao");
ctof(choose);
} else if choose.trim() == "2" {
println!("Please, Introduce a value");
io::stdin().read_line(&mut choose).expect("Please, enter an integer");
let choose: usize = choose.trim_end().parse().expect("Failed to convert to i32");
ftpc(choose);
}
}
fn ctof(c: i32) {
let celsius_to_fahrenheit: i32 = (c * (9 / 5)) + 32;
println!("Here is your conversion: {celsius_to_fahrenheit}")
}
fn ftpc(f: usize) {
let fahrenheit_to_celsius: usize = (f-32) * (5 / 9);
println!("Here is your conversion: {fahrenheit_to_celsius}")
}
'''
Using .read_line() to read into a String will append to the existing data, not overwrite it. And you used .trim() it ignore the newline in your comparisons, but it still exists; it wasn't removed from the string. So if you enter 1 and then 26, the variable choose will contain "1\n26\n". Using .trim() will not remove the newline character in the middle so .parse() will encounter an invalid digit.
You should call choose.clear() before writing into it again or else use a different variable.

Save command line argument to variable and use a default if it is missing or invalid

The idea here is simple but I have tried three different ways with different errors each time: read in a string as an argument, but if the string is invalid or the string isn't provided, use a default.
Can this be done using Result to detect a valid string or a panic?
The basic structure I expect:
use std::env;
use std::io;
fn main() {
let args: Vec<String> = args().collect();
let word: Result<String, Error> = &args[1].expect("Valid string");
let word: String = match word {
Ok(word) = word,
Err(_) = "World",
}
println!("Hello, {}", word);
}
So, there are a lot of issues in your code.
First and foremost, in a match statement, you do not use =, you use =>.
Additionally, your match statement returns something, which makes it not an executing block, but rather a returning block (those are not the official terms). That means that your blocks result is bound to a variable. Any such returning block must end with a semicolon.
So your match statement would become:
let word: String = match word {
Ok(word) => word,
Err(_) => ...,
};
Next, when you do use std::env, you do not import all of the functions from it into your namespace. All you do is that you create an alias, so that the compiler turns env::<something> intostd::env::<something> automatically.
Therefore, this needs to be changed:
let args: Vec<String> = env::args().collect();
The same problem exists in your next line. What is Error? Well, what you actually mean is io::Error, that is also not imported due to the same reasons stated above. You might be wondering now, how Result does not need to be imported. Well, it is because the Rust Team has decided on a certain set of functions and struct, which are automatically imported into every project. Error is not one of them.
let word: Result<String, io::Error> = ...;
The next part is wrong twice (or even thrice).
First of all, the operation [x] does not return a Result, it returns the value and panics if it is out-of-bounds.
Now, even if it was a result, this line would still be wrong. Why? Because you expect(...) the result. That would turn any Result into its value.
Now, what you are looking for is the .get(index) operation. It tries to get a value and if it fails, it returns None, so it returns an option. What is an option? It is like a result, but there is no error value. It must be noted that get() returns the option filled with a reference to the string.
The line should look something like this:
let word: Option<&String> = args.get(1);
Now you have two options to handle default values, but before we come to that, I need to tell you why your error value is wrong.
In Rust, there are two kinds of Strings.
There is ยด&str`, which you can create like this:
let a: &str = "Hello, World!";
These are immutable and non-borrowed strings stored on the stack. So you cannot just create a new one with arbitary values on the fly.
On the other hand, we have mutable and heap-allocated Strings.
let mut a: String = String::new();
a.push_str("Hello, World!");
// Or...
let b: String = String::from("Hello, World");
You store your arguments as a String, but in your match statement, you try to return a &str.
So, there are two ways to handle your error:
let word: Option<&String> = args.get(1);
let word: String = match word {
Some(word) => word.to_string(),
None => String::from("World"),
};
If you do not want to allocate that second string, you can also use
let word: Option<&String> = args.get(1);
let word: &str = match word {
Some(word) => word.as_str(),
None => "World",
};
The second option, unwrap_or
let args: Vec<String> = env::args().collect();
let default = &String::from("World");
let word: &String = args.get(1).unwrap_or(default);
println!("Hello, {}", word);
is a bit uglier, as it requires you to bind the default value to a variable. This will do what your match statement above does, but it's a bit prettier.
This works too:
let word: &str = args.get(1).unwrap_or(default);
So this is my favourite version of your program above:
use std::env;
fn main() {
let args: Vec<String> = env::args().collect();
let default = &String::from("World");
let word: &str = args.get(1).unwrap_or(default);
println!("Hello, {}", word);
}
But this one works too:
use std::env;
fn main() {
let args: Vec<String> = env::args().collect();
let word: Option<&String> = args.get(0);
let word: &str = match word {
Some(word) => word.as_str(),
None => "World",
};
println!("Hello, {}", word);
}

How to return a concatenated string from `String` fields?

I am trying to write a method of an object that returns a combination of String fields of that object. I have tried several approaches, as you can see in the example code, and in each case I am getting errors shown below including
expected &str, found struct std::string::String
s is borrowed here
cannot return value referencing local variable s
struct Person {
first_name: String,
last_name: String,
age: u8,
}
// BEHAVIOR OF CLASS
impl Person {
fn full_name(&self) -> &str {
let s = format!("{} {}", self.first_name, self.last_name);
// temporary, test
println!("** test, s: {}", s);
// how to return the 's' value?
// try 1. expected `&str`, found struct `std::string::String`
// s
// try 2. `s` is borrowed here
// s.as_str()
// try 3. cannot return value referencing local variable `s`
// &s[..]
// try 4. cannot return value referencing local variable `s`
// &*s
// then escape
"?"
}
// works fine
fn description(&self) -> &str {
match self.age {
0..=12 => "Clild",
13..=17 => "Teenager",
_ => "Adult",
}
}
}
fn main() {
println!("Laboratory");
let p = Person {
first_name: String::from("Alexandra"),
last_name: String::from("Ansley"),
age: 25,
};
println!("Full Name : {}", p.full_name());
println!("Description : {}", p.description());
}
How can I write this method without these errors?
Explanation for Trial 1:
fn full_name(&self) -> &str {
// format macro returns a String
let s = format!("{} {}", self.first_name, self.last_name);
s // Error: return type and given type doesn't match
}
Explanation for Trial 2:
fn full_name(&self) -> &str {
let s = format!("{} {}", self.first_name, self.last_name); // owned value
let s_str = s.as_str() // reference to s
s_str
} // s is dropped here
// s_str is now a dangling pointer, so it is rightly refused by the borrow checker
Explanation for Trial 3:
fn full_name(&self) -> &str {
let s = format!("{} {}", self.first_name, self.last_name); // owned value
let s_str = &s[...] // is equivalent to s.as_str()
s_str // reference to s
} // s is dropped here
// s_str is now a dangling pointer, so it is rightly refused by the borrow checker
Explanation for Trial 4:
impl Person {
fn full_name(&self) -> String {
let s = format!("{} {}", self.first_name, self.last_name);
let s_str = &*s // is equivalent to &*std::ops::Deref(&s), which is of type &str
// see https://doc.rust-lang.org/reference/expressions/operator-expr.html#the-dereference-operator
s_str // reference to s
} // s is dropped here
}
// s_str is now a dangling pointer, so it is rightly refused by the borrow checker
You need to return a String instead of &str since:
You can't return reference to an owned value created inside a function definition block without creating a dangling pointer.
and you can't concatenate two &str together without allocating both of them into a String, because &str needs to be contiguous memory (see docs)
(Given your comments, I think you already know everything here. I'm just writing for later searchers.)
fn full_name(&self) -> &str { ... }
The most important fact about this signature is that it must return a reference to existing memory. It can't create any new thing. The whole point of &str is "a pointer to something that will exist for at least as long as you have this reference."
So this would be fine if there were a property on this struct that held onto full_name. But if the function constructs "the full name," this is impossible.
The most natural thing to do here is return String (and that's my recommendation). That's an owned value, so you can create it in the function and transfer it to the caller.
Another approach would be to have a full_name property that started empty, and was lazily filled in when requested and returned as a &str, with the promise that it would live as long as the struct.

Using rustc_serialize and getting unquoted strings

So I have gone through 90% of the tutorial on Rust and I think I mostly have a grasp on the syntax. I'm attempting to start writing code with it I'm currently using the rustc_serialize library to parse JSON from stdin and I'm not getting the results I expect. I have the following JSON file called message.txt the following content:
{"text": "hello world"}
Here is the Rust code to accept stdin and parse out the text field:
extern crate rustc_serialize;
use std::io::{self, Read};
use rustc_serialize::json::Json;
fn main() {
// provide a buffer for stdin
let mut buffer = String::new();
let _ = io::stdin().read_to_string(&mut buffer);
// parse the json
let message = match Json::from_str(&mut buffer) {
Ok(m) => m,
Err(_) => panic!("Stdin provided invalid JSON")
};
// get the message object and "text" field string
let message_object = message.as_object().unwrap();
let message_string = message_object.get("text").unwrap();
println!("{}", message_string);
println!("{}", &message_string.to_string()[0..4]);
}
The following code outputs:
"Hello World"
"Hel
I'm currently outputting the byte slice to make sure the quote wasn't something that was added by print. According to the docs message_string shouldn't have quotes around it.
If I print out the data using the example from the documentation then it prints the value of "text" without quotes:
for (key, value) in message_object.iter() {
println!("{}: {}", key, match *value {
Json::U64(v) => format!("{} (u64)", v),
Json::String(ref v) => format!("{} (string)", v),
_ => format!("other")
});
}
Output:
text: hello world (string)
I'm a newbie to Rust so I probably just don't understand the string manipulation parts of Rust all that well.
The problem is that message_string isn't what you think it is. I discovered that when I tried to use len on the "string", which didn't work (I assume that's why you have a to_string when you are slicing). Let's make the compiler tell us what it is:
let () = message_string;
Has the error:
error: mismatched types:
expected `&rustc_serialize::json::Json`,
found `()`
It's a Json! We need to convert that enumerated type into a string-like thing:
let message_object = message.as_object().unwrap();
let message_json = message_object.get("text").unwrap();
let message_string = message_json.as_string().unwrap();
Ultimately, I'd argue that Display (which allows the {} format string) should not have been implemented for this type, as Display means format in an end-user-focused manner. It's probably too late to change that decision now though.
I know that unwrap is great for quick prototyping, but I'd be remiss in not showing a slightly more idiomatic way of doing this:
fn main() {
let mut buffer = String::new();
io::stdin().read_to_string(&mut buffer).expect("Could not read from stdin");
let message = Json::from_str(&mut buffer).expect("Stdin provided invalid JSON");
let message_string = message.as_object().and_then(|obj| {
obj.get("text").and_then(|json| {
json.as_string()
})
}).expect("The `text` key was missing or not a string");
println!("{}", message_string);
}
Ignoring the Result from read_to_string is worse than panicking. ^_^

Understanding scope and shadowing matches

I'm trying to improve on the final guessing game sample code a bit. Particularly, I plan to output "Please input a number!" if the user does not input a number rather than "Please input your guess." again. I'm doing this with an inner loop. The code below does work:
let guess: u32;
loop {
let mut guess_str = String::new();
io::stdin().read_line(&mut guess_str)
.ok()
.expect("Failed to read line");
guess = match guess_str.trim().parse() {
Ok(num) => num,
Err(_) => {
println!("Please input a number!");
continue;
}
};
break;
}
I'd like to avoid the guess_str if I can by properly shadowing the matches. If I change guess_str to guess, Rust complains of use of possibly uninitialized variable: `guess`. I'm not sure how the variable could possibly be uninitialized if it's impossible for it to not be uninitialized with the code above. Is there any way to do this only using guess?
Let's look at a simpler reproduction:
fn make_guess() -> u32 {
let guess;
{
let mut guess;
guess = 1;
}
guess
}
Here, you create an outer variable guess and then shadow it inside the block. When you assign the value 1 to guess, you are assigning to the inner variable. The outer variable is never set to anything, thus you end up with the "use of possibly uninitialized variable" error.
Is there any way to only use one variable
Indirectly, yes. I'd extract the code to a function. When you have a successful guess, you can simply return. Otherwise you allow the loop to occur:
fn make_guess() -> u32 {
loop {
let mut guess = String::new();
io::stdin().read_line(&mut guess)
.ok()
.expect("Failed to read line");
match guess.trim().parse() {
Ok(num) => return num,
Err(_) => {
println!("Please input a number!");
}
}
}
}
This avoids the shadowing completely, avoids having to use an explicit continue, and adds a small amount of abstraction and organization to your code.

Resources