I'm new in rust and i faced with some strange behavior of IDE IntelliJ IDEA 2021.3.3
I started from https://doc.rust-lang.org/book/ch02-00-guessing-game-tutorial.html and playing with IDE when i find that if i debug my code and shadowing the variable user_guess from String to u32 - variable inspector in IDE doesn't see that change and think that it is String
Does it looks like IDE bug, or what?
Full listing :
use rand::Rng;
use std::cmp::Ordering;
use std::{cmp, io};
fn main() {
println!("Lets play some game");
let ai_guess: u32 = rand::thread_rng().gen_range(0..=100);
loop {
let mut user_guess = String::new();
io::stdin().read_line(&mut user_guess).expect("Error");
let user_guess: u32 = match user_guess.trim().parse() {
Ok(num) => num,
Err(_) => 99
};
println!("{}",user_guess.to_string());
match user_guess.cmp(&ai_guess) {
Ordering::Less => { println!("You number is less then my") }
Ordering::Equal => {
println!("You WON!");
break;
}
Ordering::Greater => { println!("You bigger is less then my") }
}
};
}
Related
I have a little code snippet where I'm trying to write struct to a file and then read it. I have seen other similar posts where the asker has forgotten to zero initialise the buffer they are trying to read into. I have made sure not to do this, but I still am getting the error that 'failed to fill whole buffer' error when using read_exact, even though my buffer size and the size of the file I'm trying to read are the same.
Here is the code:
use std::fs::{File, OpenOptions};
use std::io::prelude::*;
use bincode::*;
use serde::{Serialize, Deserialize};
#[derive(Serialize, Deserialize, PartialEq, Debug)]
pub struct Node {
pub name: String,
end_ptr: u32 // number of bytes away the next node is
}
impl Node {
pub fn to_string(&self) -> String {
return self.name.clone();
}
}
fn main() {
let node = Node { name: String::from("node_1"), end_ptr: 0 };
let node_as_buf = bincode::serialize(&node).unwrap();
let len_of_bytes_serialised: usize = node_as_buf.len();
let mut file = OpenOptions::new()
.read(true)
.write(true)
.create(true)
.truncate(true)
.open("test.txt")
.unwrap();
match file.write_all(&node_as_buf) {
Ok(result) => {println!("Write success")},
Err(err) => {println!("{}", &err)}
}
println!("{}", file.metadata().unwrap().len()); // this and
println!("{}", len_of_bytes_serialised); // this are the same size
let mut buffer = vec![0; len_of_bytes_serialised];
match file.read_exact(&mut buffer[..]) {
Ok(result) => println!("Read success"),
Err(err) => println!("{}", &err) // prints 'failed to fill whole buffer'
}
let read_node: Node = bincode::deserialize(&buffer[..]).unwrap();
}
Because you use the same stream for reading and writing, the cursor is moved to the end of the file and any reading will attempt to read there, which will of course fail. You can observe that if you'll print file.stream_position().
You need to rewind() before reading.
I know the idiomatic way to handle errors in rust is using match statement but I am looking for other ways to do the same.
use std::fs;
fn main() {
if let Ok(f) = fs::read_dir("/dummy"){
println!("First: {:?}", f);
}else{
println!("Error");
}
}
This works but I need the original Result error from fs::read_dir("/dummy") to be printed in the else statement. How can I do it?
Generally, I'd consider such an approach a bad idea, but you do have a few options, the most obvious two being "multiple if lets" and a "functional style" approach. I've included the match version for comparison. The code is available on the playground.
fn multiple_if_lets() {
let f = std::fs::read_dir("/dummy");
if let Ok(f) = &f {
println!("First: {:?}", f);
}
if let Err(f) = &f {
println!("Error: {:?}", f);
}
}
fn functional_style() {
std::fs::read_dir("/dummy")
.map(|f| println!("First: {:?}", f))
.unwrap_or_else(|f| println!("Error: {:?}", f));
}
fn match_style() {
match std::fs::read_dir("/dummy") {
Ok(f) => println!("First: {:?}", f),
Err(f) => println!("Error: {:?}", f),
}
}
I'm not quite sure why you don't want to use match because it is like a better if let! But you might find it useful to use an external crate like anyhow. You can very easily propagate all errors as one type and also add context where it makes sense.
In your Cargo.toml
[dependencies]
anyhow = "1"
In your code
use std::fs;
use anyhow::Context;
fn main() -> anyhow::Result<()> {
let dir = fs::read_dir("/dummy").context("failed to read dir")?;
// another fallible function which can return a `Result` with a
// different error type.
do_something(dir).context("failed to do something")?;
Ok(())
}
If read_dir had to fail here your program would exit and it would output the following
Error: failed to read dir
Caused by:
No such file or directory (os error 2)
If you wanted to throw away the error but still print it out you could still use match.
let dir = match fs::read_dir("/dummy").context("failed to read dir") {
Ok(dir) => Some(dir),
Err(err) => {
eprintln!("Error: {:?}", err);
None
}
};
This would output something like the following but still continue:
Error: failed to read dir
Caused by:
No such file or directory (os error 2)
Since Rust 1.65.0 (Nov. 2022), as mentioned by Mara Bos, you can do:
let Ok(f) = fs::read_dir("/dummy") else{ println!("Error"); return };
println!("First: {:?}", f);
let-else statements.
You can now write things like:
let Ok(a) = i32::from_str("123") else { return };
without needing an if or match statement.
This can be useful to avoid deeply nested if statements.
Let's say I have a attrs: Vec<Attribute> of some function attributes and a function fn map_attribute(attr: &Attribute) -> Result<TokenStream, Error> that maps the attributes to some code.
I know that I could write something like this:
attrs.into_iter()
.map(map_attribute)
.collect::<Result<Vec<_>, _>()?
However, this is not what I want. What I want is spit out all errors at once, not stop with the first Error. Currently I do something like this:
let mut codes : Vec<TokenStream> = Vec::new();
let mut errors: Vec<Error> = Vec::new();
for attr in attrs {
match map_attribute(attr) {
Ok(code) => codes.push(code),
Err(err) => errors.push(err)
}
}
let mut error_iter = errors.into_iter();
if let Some(first) = error_iter.nth(0) {
return Err(iter.fold(first, |mut e0, e1| { e0.combine(e1); e0 }));
}
This second version does what I want, but is considerably more verbose than the first version. Is there a better / more idiomatic way to acchieve this, if possible without creating my own iterator?
The standard library does not have a convenient one-liner for this as far as I know, however the excellent itertools library does:
use itertools::Itertools; // 0.9.0
fn main() {
let foo = vec![Ok(42), Err(":("), Ok(321), Err("oh noes")];
let (codes, errors): (Vec<_>, Vec<_>)
= foo.into_iter().partition_map(From::from);
println!("codes={:?}", codes);
println!("errors={:?}", errors);
}
(Permalink to the playground)
I ended up writing my own extension for Iterator, which allows me to stop collecting codes when I encounter my first error. This is in my use case probably a bit more efficient than the answer by mcarton, since I only need the first partition bucket if the second one is empty. Also, I need to fold the errors anyways if I want to combine them into a single error.
pub trait CollectToResult
{
type Item;
fn collect_to_result(self) -> Result<Vec<Self::Item>, Error>;
}
impl<Item, I> CollectToResult for I
where
I : Iterator<Item = Result<Item, Error>>
{
type Item = Item;
fn collect_to_result(self) -> Result<Vec<Item>, Error>
{
self.fold(<Result<Vec<Item>, Error>>::Ok(Vec::new()), |res, code| {
match (code, res) {
(Ok(code), Ok(mut codes)) => { codes.push(code); Ok(codes) },
(Ok(_), Err(errors)) => Err(errors),
(Err(err), Ok(_)) => Err(err),
(Err(err), Err(mut errors)) => { errors.combine(err); Err(errors) }
}
})
}
}
When trying to open a broken epub/ZIP file with epub-rs, the zip-rs crate error (which doesn't use Failure) is wrapped into a failure::Error by epub-rs. I want to handle each error type of zip-rs with an distinct error handler and need a way to match against the underlying error. How can I retrieve it from Failure?
fn main() {
match epub::doc::EpubDoc::new("a.epub") {
Ok(epub) => // do something with the epub
Err(error) => {
// handle errors
}
}
}
error.downcast::<zip::result::ZipError>() fails and error.downcast_ref() returns None.
You can downcast from a Failure Error into another type that implements Fail by using one of three functions:
downcast
downcast_ref
downcast_mut
use failure; // 0.1.5
use std::{fs, io};
fn generate() -> Result<(), failure::Error> {
fs::read_to_string("/this/does/not/exist")?;
Ok(())
}
fn main() {
match generate() {
Ok(_) => panic!("Should have an error"),
Err(e) => match e.downcast_ref::<io::Error>() {
Some(e) => println!("Got an io::Error: {}", e),
None => panic!("Could not downcast"),
},
}
}
For your specific case, I'm guessing that you are either running into mismatched dependency versions (see Why is a trait not implemented for a type that clearly has it implemented? for examples and techniques on how to track this down) or that you simply are getting the wrong error type. For example, a missing file is actually an std::io::Error:
// epub = "1.2.0"
// zip = "0.4.2"
// failure = "0.1.5"
use std::io;
fn main() {
if let Err(error) = epub::doc::EpubDoc::new("a.epub") {
match error.downcast_ref::<io::Error>() {
Some(i) => println!("IO error: {}", i),
None => {
panic!("Other error: {} {:?}", error, error);
}
}
}
}
I'd like to use map to iterate over an array and do stuff per item and get rid of the for loop. An error which I do not understand blocks my attempt. What I want to achieve is to iterate through a vector of i32 and match on them to concat a string with string literals and then return it at the end.
Function:
pub fn convert_to_rainspeak(prime_factors: Vec<i32>) -> String {
let mut speak = String::new();
prime_factors.iter().map(|&factor| {
match factor {
3 => { speak.push_str("Pling"); },
5 => { speak.push_str("Plang"); },
7 => { speak.push_str("Plong"); },
_ => {}
}
}).collect();
speak
}
fn main() {}
Output:
error[E0282]: type annotations needed
--> src/main.rs:10:8
|
10 | }).collect();
| ^^^^^^^ cannot infer type for `B`
Iterator::collect is defined as:
fn collect<B>(self) -> B
where
B: FromIterator<Self::Item>
That is, it returns a type that is up to the caller. However, you have completely disregarded the output, so there's no way for it to infer a type. The code misuses collect when it basically wants to use for.
In your "fixed" version (which has since been edited, making this paragraph make no sense), you are being very inefficient by allocating a string in every iteration. Plus you don't need to specify any explicit types other than those on the function, and you should accept a &[i32] instead:
fn convert_to_rainspeak(prime_factors: &[i32]) -> String {
prime_factors.iter()
.map(|&factor| {
match factor {
3 => "Pling",
5 => "Plang",
7 => "Plong",
_ => "",
}
})
.collect()
}
fn main() {
println!("{}", convert_to_rainspeak(&[1, 2, 3, 4, 5]));
}
The error in this case is that the compiler can't figure out what type you are collecting into. If you add a type annotation to collect, then it will work:
pub fn convert_to_rainspeak(prime_factors:Vec<i32>) -> String{
let mut speak = String::new();
prime_factors.iter().map(| &factor| {
match factor {
3 => {speak.push_str("Pling");},
5 => {speak.push_str("Plang");},
7 => {speak.push_str("Plong");},
_ => {}
}
}).collect::<Vec<_>>();
speak
}
However, this is really not the idiomatic way to do this. You should use for instead:
pub fn convert_to_rainspeak(prime_factors:Vec<i32>) -> String {
let mut speak = String::new();
for factor in prime_factors.iter() {
match *factor {
3 => speak.push_str("Pling"),
5 => speak.push_str("Plang"),
7 => speak.push_str("Plong"),
_ => {}
}
}
speak
}
You could also use flat_map() instead of map().
This way you can map to Option and return None instead of empty string if there is no corresponding value.
fn convert_to_rainspeak(prime_factors: &[i32]) -> String {
prime_factors
.iter()
.flat_map(|&factor| match factor {
3 => Some("Pling"),
5 => Some("Plang"),
7 => Some("Plong"),
_ => None,
})
.collect()
}
fn main() {
println!("{}", convert_to_rainspeak(&[1, 2, 3, 7]));
}
few minutes later I solved it myself (any upgrade appreciated):
pub fn convert_to_rainspeak(prime_factors:Vec<i32>) -> String{
let mut speak:String = prime_factors.iter().map(|&factor| {
match factor {
3 => {"Pling"},
5 => {"Plang"},
7 => {"Plong"},
_ => {""}
}
}).collect();
speak
}
The issue was that I was not aware that .collect() is awaiting the result of map. I then assigned prime_factors.iter()... to a string and rearranged var bindings so it now all works.
EDIT: refactored redundant assignments to the speak vector