rust create a String from file.read_to_end() - rust

I am trying to read a file and return it as a UTF-8 std:string:String it seems like content is a Result<collections::string::String, collections::vec::Vec<u8>> if i understand an error message i got from trying String::from_utf8(content).
fn get_index_body () -> String {
let path = Path::new("../html/ws1.html");
let display = path.display();
let mut file = match File::open(&path) {
Ok(f) => f,
Err(err) => panic!("file error: {}", err)
};
let content = file.read_to_end();
println!("{} {}", display, content);
return String::new(); // how to turn into String (which is utf-8)
}

Check the functions provided by the io::Reader trait: http://doc.rust-lang.org/std/io/trait.Reader.html
read_to_end() returns IoResult<Vec<u8>>, read_to_string() returns IoResult<String>.
IoResult<String> is just a handy way to write Result<String, IoError>: http://doc.rust-lang.org/std/io/type.IoResult.html
You can extract Strings from a Result either using unwrap():
let content = file.read_to_end();
content.unwrap()
or by handling the error yourself:
let content = file.read_to_end();
match content {
Ok(s) => s,
Err(why) => panic!("{}", why)
}
See also: http://doc.rust-lang.org/std/result/enum.Result.html

Related

How call function on Err arm

I wrote this code and it works. But I have a custom class for outputting logs to stdout. I want to call my logging function if I get an Error instead of panic! macro.
fn create_logfile() -> File {
let timestamp = Log::format_date(chrono::Local::now());
let filename = format!("{}.log", timestamp);
let logfile = match File::create(filename) {
Ok(file) => file,
Err(error) => {
panic!("There was a problem creating the file: {:?}", error)
}
};
logfile
}
For example I want get something like that:
let logfile = match File::create(filename) {
Ok(file) => file,
Err(e) => {
Log::error("Log file creation failed, reason: {}", e);
process::exit(1)
}
};
But compiler says:
[E0308] `match` arms have incompatible types.
[Note] expected struct `File`, found `()
How can I solve this problem?
If I put the error data to stderr will it help?
Your revised example with std::process::exit() works for me: (link)
use chrono; // 0.4.23
use std::fs::File;
use log; // 0.4.17
struct Log { }
impl Log {
fn format_date(date: chrono::DateTime<chrono::offset::Local>) -> i64 {
return 0;
}
}
fn old_create_logfile() -> File {
let timestamp = Log::format_date(chrono::Local::now());
let filename = format!("{}.log", timestamp);
let logfile = match File::create(filename) {
Ok(file) => file,
Err(error) => {
panic!("There was a problem creating the file: {:?}", error)
}
};
logfile
}
fn new_create_logfile() -> File {
let timestamp = Log::format_date(chrono::Local::now());
let filename = format!("{}.log", timestamp);
let logfile = match File::create(filename) {
Ok(file) => file,
Err(e) => {
// Instead of using `Log::error`, we'll use `log::error!` for show.
log::error!("Log file creation failed, reason: {}", e);
// This compiles.
std::process::exit(1)
}
};
logfile
}
fn main() {
new_create_logfile();
}
Normally, you need to make sure the return types of all match arms have the same type -- here, you are returning std::fs::File under the Ok branch, so the Err branch can "escape" the requirement by returning ! (pronounced "never") (link).
Since computation never returns from the std::process::exit() and its return type is marked as !, it's passes the type-checking stage.

How to do I proper Error-Handling for a File-Reader in Rust

I would like to load a Json file to a Vec.
When I do everything with unwrap() it works fine. Now I want to Catch some Errors like "File not Found" and Handle them, but I'm not able to figure out how to do it probably.
Maybe you could help me? Thanks in advance.
fn read_from_file(filename: &str) -> Result<Vec<Card>, io::Error> {
let f = File::open(filename);
let mut f = match f {
Ok(file) => file,
Err(e) => return Err(e),
};
let mut s = String::new();
f.read_to_string(&mut s)?;
let data = serde_json::from_str(&s)?;
Ok(data)
}
fn main() {
let mut cards = read_from_file("Test.json");
let mut cards = match cards {
Ok(Vec) => Vec,
Err(_) => {
println!(
"File was not found!\n
Do you want to create a new one? (y/N)"
);
let mut answer = String::new();
io::stdin()
.read_line(&mut answer)
.expect("Failed to read line");
answer.pop();
if answer == "y" {
let mut cards: Vec<Card> = Vec::new();
return cards;
} else {
panic!("No file was found and you did not allow to create a new one");
}
}
};
}
You need to check what the ErrorKind value is. If it is NotFound, then you know the file doesn't exist.
In your code:
let f = File::open(filename);
let mut f = match f {
Ok(file) => file,
Err(e) => return Err(e),
};
, where you have Err(e) match arm, the type of e is std::io::Error. That struct has a method kind() which is what you need to check. Like this:
let mut f = match f {
Ok(file) => file,
Err(e) => {
if e.kind() == ErrorKind::NotFound {
println!("Oh dear, the file doesn't exist!");
}
...
}
};

How do I convert from &alloc::string::String to a string literal?

The title says it all really. I need to convert from &alloc::string::String to a string literal (&str I think) according to the error I am getting when I am trying to write to a file. How do I convert what I have into that?
The overall goal here is to read from one file and line by line append to another.
Full code:
use std::{
fs::File,
io::{self, BufRead, BufReader},
fs::OpenOptions,
fs::write,
any::type_name,
path::Path,
io::Write,
};
fn type_of<T>(_: T) -> &'static str {
type_name::<T>()
}
fn main(){
let inpath = Path::new("tool_output.txt");
let outpath = Path::new("test_output.txt");
let indisplay = inpath.display();
let outdisplay = outpath.display();
let mut infile = match File::open(&inpath) {
Err(why) => panic!("couldn't open {}: {}", indisplay, why),
Ok(infile) => infile,
};
let mut outfile = match OpenOptions::new().write(true).append(true).open(&outpath) {
Err(why) => panic!("couldn't open {}: {}", outdisplay, why),
Ok(outfile) => outfile,
};
let reader = BufReader::new(infile);
for line in reader.lines() {
let format_line = String::from(line.unwrap()); // <- I thought this would fix the error but it didnt.
println!("Type = {}", type_of(&format_line));
let _ = writeln!(outfile, &format_line).expect("Unable to write to file"); <- this is currently causing the error.
//write("test_output.txt", line.unwrap()).expect("Unable to write to file");
}
}
error:
error: format argument must be a string literal
--> text_edit.rs:36:28
|
36 | let _ = writeln!(outfile, format_line).expect("Unable to write to file");
| ^^^^^^^^^^^
|
A string literal is what it says - a literal so "literal" is a string literal. To use writeln macro to write a string, you have to do writeln!(outfile, "{}", line) and here "{}" is the format string literal. If you’ve ever used println macro, it’s basically that but you specify what stream to print to.

How can I check a variable entered by user is a number (int, float)?

How can I check a variable (entered by user) is a number such as an int, float, or something else?
I want to do this with a match expression:
let mut input = String::new();
io::stdin().read_line(&mut input);
let result = match input {
// !!??
}
is it possible by match?
If you want to match against something then you need something to destructure on. You can match against string slices, but only a finite set, so that doesn't help here.
So let's have an enum to match on:
enum Value {
Int(isize),
Float(f64),
}
use Value::*; // Make the variants available without Value::
Then you need to parse the string into the appropriate type. You can use the parse method for each type (if you're happy with Rust's syntax for those; otherwise you might need a fancier parser):
fn parse_string(s: &str) -> Option<Value> {
if let Ok(i) = s.parse() { // inferred as isize from next line
Some(Int(i))
} else if let Ok(f) = s.parse() {
Some(Float(f))
} else {
None
}
}
Note that when trying parses in sequence like this the order matters; "123" would parse as an f64 too.
I'm turning any parse errors into None and using Option rather than Result because it's not clear what the error would be (since each parse can return its own); in a real application I might have a ParseError type.
Now we can match on the result:
fn main() {
let x = "123";
match parse_string(x) {
Some(Int(i)) => println!("int {}", i),
Some(Float(f)) => println!("float {}", f),
None => println!("Didn't parse"),
}
let x = "123.5";
match parse_string(x) {
Some(Int(i)) => println!("int {}", i),
Some(Float(f)) => println!("float {}", f),
None => println!("Didn't parse"),
}
}
Runnable playground link

How to simplify parsing a text file to a vector of values?

I'm new to Rust and I'm trying to find the most simple and effective way of parsing text file like:
1
2
3
4
5
to a vector of u32 in my code. For now I have a solution for reading a file as string (it's just right from rustbyexample.com, I changed it a little):
let path = Path::new("J:/IntegerArray.txt");
let display = path.display();
let mut file = match File::open(&path)
{
Err(why) => panic!("couldn't open {}: {}", display, why.desc),
Ok(file) => file,
};
let data_str = match file.read_to_string()
{
Err(why) => panic!("couldn't read {}: {}", display, why.desc),
Ok(string) =>
{
string
}
};
Then I parse it:
let mut data : Vec<u32> = vec![];
for str in data_str.lines_any()
{
data.push(match str.trim().parse() { Some(x) => x, None => continue, } );
}
However I think there's a solution where it could be done in one line without a loop, something like:
let mut data : Vec<u32> = data_str.lines_any().<SOME MAGIC>.collect();
Maybe it can be done with map and filter, but the main problem is in unwrapping Option to u32, because I can't see how to filter away Nones and unwrap to u32 at the same time. Otherwise, just filtering without unwrapping leads to checking for them again further. Is a one-line solution possible? And will it be an effective solution?
filter_map is what you're looking for:
let data: Vec<u32> = data_str.lines_any().filter_map(|s| s.trim().parse()).collect();

Resources