This question already has answers here:
Return local String as a slice (&str)
(7 answers)
Closed 7 years ago.
I'm trying to read in some external GLSL code into Rust. The reading works properly, but I run into a lifetime issue in the final expression (in the Ok(_) branch)
error: s does not live long enough
fn read_shader_code(string_path: &str) -> &str {
let path = Path::new(string_path);
let display = path.display();
let mut file = match File::open(&path) {
Err(why) => panic!("Couldn't open {}: {}", display, Error::description(&why)),
Ok(file) => file,
};
let mut s = String::new();
match file.read_to_string(&mut s) {
Err(why) => panic!("couldn't read {}: {}", display, Error::description(&why)),
Ok(_) => &s,
}
}
The string bound to "s" will be deallocated once the function ends ("s" goes out of scope), so you cannot return a reference to its contents outside the function.
The best way is to return the string itself:
fn read_shader_code(string_path: &str) -> String {
let path = Path::new(string_path);
let display = path.display();
let mut file = match File::open(&path) {
Err(why) => panic!("Couldn't open {}: {}", display, Error::description(&why)),
Ok(file) => file,
};
let mut s = String::new();
match file.read_to_string(&mut s) {
Err(why) => panic!("couldn't read {}: {}", display, Error::description(&why)),
Ok(_) => s,
}
}
Related
I have a HashMap with different hashing algorithms which implement digest::DynDigest, but when they are looped over and update()d the final hash is always the same (Sha224 d14a...) (you can change the file PathBuf parameter to anything and get the exact same hash every time). So the update() and/or finalize() is not updating the instance in the HashMap.
What is the correct way to loop over the different hashers so that HashMap keeps correct updated state?
use digest::{Digest, DynDigest}; // https://crates.io/crates/digest/reverse_dependencies
use sha2; // implements digest::DynDigest
use std::collections::HashMap;
use std::fs::File;
use std::io;
use std::io::{BufReader, Read};
use std::path::PathBuf;
fn checksum_to_hex(bytes: &[u8]) -> String {
let mut s: String = String::new();
for b in bytes {
s.push_str(format!("{:02x}", b).as_str());
}
s
}
pub fn hash_file(
fpath: PathBuf,
hashers: HashMap<&str, Box<dyn DynDigest>>,
) -> io::Result<HashMap<&str, String>> {
let f = File::open(fpath).expect("failed to open file for hashing");
let mut buffer = [0u8; 1048576];
let mut reader = BufReader::new(f);
loop {
let count = reader.read(&mut buffer)?;
if count == 0 {
break;
}
for (_, mut hasher) in hashers.to_owned() {
hasher.update(&buffer[..count]);
}
}
// Hash results per hasher
let mut hashes: HashMap<&str, String> = HashMap::new();
for (k, hasher) in hashers.to_owned() {
let res = checksum_to_hex(hasher.finalize().to_vec().as_slice());
hashes.insert(k, res);
}
Ok(hashes)
}
fn main() {
let mut hashers: HashMap<&str, Box<dyn DynDigest>> = HashMap::new();
hashers.insert("Sha224", Box::new(sha2::Sha224::new()));
hashers.insert("Sha256", Box::new(sha2::Sha256::new()));
hashers.insert("Sha512", Box::new(sha2::Sha512::new()));
for (htype, hashres) in hash_file(PathBuf::from("/bin/ls"), hashers).expect("") {
println!(" {} {}", htype, hashres);
}
}
Rust Playground
You need to replace the first hashers.to_owned() with hashers.iter_mut():
pub fn hash_file(
fpath: PathBuf,
mut hashers: HashMap<&str, Box<dyn DynDigest>>,
) -> io::Result<HashMap<&str, String>> {
let f = File::open(fpath).expect("failed to open file for hashing");
let mut buffer = [0u8; 1048576];
let mut reader = BufReader::new(f);
loop {
let count = reader.read(&mut buffer)?;
if count == 0 {
break;
}
for (_, hasher) in hashers.iter_mut() {
// or `for hasher in hashers.values_mut()`
hasher.update(&buffer[..count]);
}
}
// Hash results per hasher
let mut hashes: HashMap<&str, String> = HashMap::new();
for (k, hasher) in hashers {
let res = checksum_to_hex(hasher.finalize().to_vec().as_slice());
hashes.insert(k, res);
}
Ok(hashes)
}
playground
.to_owned() will clone (create an independent deep copy) the map. When you iterate over that, you're iterating through a different map than the one you eventually return.
Instead, you want to iterate over references to the elements: this is what .iter_mut() (or .values_mut() if you only need the values) will give you.
This way, you don't need the second .to_owned(), but you do need to mark the hashers argument as mutable.
This question already has an answer here:
Multiple return types in rust [duplicate]
(1 answer)
Closed 10 months ago.
I'm trying to implement a function in rust that iterates either on the StdIn Lines or a BuffReader Lines.
Both types have lines.next() method. After that, the code is just String manipulation that works.
I originally made it worked (with repetitive and implicitly-typed branches) but the two branches had similar code. This gave me the impression I can DRY/refactor my code better.
The problem is (I think) that my two match branches, in main, do not return exactly the same type. Is it possible to make them "stick together" because I'm only using one common method? (It reminds me of Python duck typing)
Here's my refacto try:
use clap::Parser;
use exitcode;
use std::any::Any;
use std::fs::File;
use std::io::{self, BufRead, BufReader, Lines, Write};
fn colorist(lines: &mut dyn Any, mut writer: impl std::io::Write) {
while let Some(line_wrap) = lines.next() {
match line_wrap {
Ok(line) => {
// ...
let buffer = "Some value base on line";
match writeln!(writer, "{}", buffer){
Ok(()) => (),
Err(e) => {eprintln!("{:?}", e);}
};
},
Err(e) => {eprintln!("{:?}", e);}
}
}
}
fn main() {
let args = Args::parse(); // have one '--file' arg being a filepath or is a empty str ("")
let mut writer = io::stdout();
let mut lines: Lines<dyn Any> = match args.file.is_empty() { // either read from stdin or read the file
false => {
let file = File::open(args.file).unwrap();
let reader = BufReader::new(file);
reader.lines() // type `std::io::Lines<BufReader<File>>`
},
true => {
let stdin = io::stdin();
stdin.lock().lines() // type `std::io::Lines<StdinLock<'_>>`
}
};
colorist(&mut lines, &mut writer);
}
This is a toy project, and I'm still learning rust on my free time. I might have some bad designs there and here.
As #PitaJ & #Chayim-friedman suggested, Either solved my problem.
I ended up with:
use either::*;
fn colorist(lines: &mut Either<Lines<BufReader<File>>, Lines<StdinLock>>, mut writer: impl std::io::Write) {...}
fn main() {
let args = Args::parse();
let mut writer = io::stdout();
let stdin = io::stdin();
let mut lines = match args.file.is_empty(){
false => {
let file = File::open(args.file).unwrap();
let reader = BufReader::new(file);
either::Left(reader.lines())
},
true => {
either::Right(stdin.lock().lines())
}
};
colorist(&mut lines, &mut writer);
match write!(writer, "{}", RESET){
_ => (),
};
let _ = writer.flush();
std::process::exit(exitcode::OK);
}
I want to use the leveldb database in Rust. Everything works fine if I have all the code in one function, but I want to split the code up and have different functions for creating an entry and reading from the db. The easiest method I thought of, was to return the database instance I created in create_database and then submit it as a parameter to the function. The Problem is that Rust doesn't allow me to use Database as a type.
This works:
use std::{env, fs};
use leveldb::database::Database;
use leveldb::iterator::Iterable;
use leveldb::kv::KV;
use leveldb::options::{Options, WriteOptions, ReadOptions};
pub fn create_database() {
let mut dir = env::current_dir().unwrap();
dir.push("demo");
let path_buf = dir.clone();
fs::create_dir_all(dir).unwrap();
let path = path_buf.as_path();
let mut options = Options::new();
options.create_if_missing = true;
// Create Database
let database = match Database::open(path, options) {
Ok(db) => {db},
Err(e) => {panic!("Failed to open database: {:?}", e)}
};
// Read from database
let read_opts = ReadOptions::new();
let res = database.get(read_opts, 1);
match res {
Ok(data) => {
assert!(data.is_some());
assert_eq!(data, Some(vec![1]));
}
Err(e) => {panic!("Failed to read from database: {:?}", e)}
};
let read_opts = ReadOptions::new();
let mut iter = database.iter(read_opts);
let entry = iter.next();
assert_eq!(
entry,
Some((1, vec![1]))
);
// Write to database
let write_ops = WriteOptions::new();
match database.put(write_ops, 1, &[1]) {
Ok(_) => {()},
Err(e) => {panic!("Failed to write to database: {:?}", e)}
};
}
But this doesn't:
use std::{env, fs};
use leveldb::database::Database;
use leveldb::iterator::Iterable;
use leveldb::kv::KV;
use leveldb::options::{Options, WriteOptions, ReadOptions};
pub fn create_database() -> Database {
let mut dir = env::current_dir().unwrap();
dir.push("demo");
let path_buf = dir.clone();
fs::create_dir_all(dir).unwrap();
let path = path_buf.as_path();
let mut options = Options::new();
options.create_if_missing = true;
// Create Database
let database = match Database::open(path, options) {
Ok(db) => {db},
Err(e) => {panic!("Failed to open database: {:?}", e)}
};
return database;
}
pub fn get(database: Database) {
// Read from database
let read_opts = ReadOptions::new();
let res = database.get(read_opts, 1);
match res {
Ok(data) => {
assert!(data.is_some());
assert_eq!(data, Some(vec![1]));
}
Err(e) => {panic!("Failed to read from database: {:?}", e)}
};
let read_opts = ReadOptions::new();
let mut iter = database.iter(read_opts);
let entry = iter.next();
assert_eq!(
entry,
Some((1, vec![1]))
);
}
pub fn put(database: Database) {
// Write to database
let write_ops = WriteOptions::new();
match database.put(write_ops, 1, &[1]) {
Ok(_) => {()},
Err(e) => {panic!("Failed to write to database: {:?}", e)}
};
}
If I execute this program I get this error:
error[E0107]: wrong number of type arguments: expected 1, found 0
--> src/db/db.rs:8:29
|
8 | pub fn create_database() -> Database {
| ^^^^^^^^ expected 1 type argument
error[E0107]: wrong number of type arguments: expected 1, found 0
--> src/db/db.rs:27:22
|
27 | pub fn get(database: Database) {
| ^^^^^^^^ expected 1 type argument
error[E0107]: wrong number of type arguments: expected 1, found 0
--> src/db/db.rs:48:22
|
48 | pub fn put(database: Database) {
| ^^^^^^^^ expected 1 type argument
error: aborting due to 3 previous errors
For more information about this error, try `rustc --explain E0107`.
error: could not compile `gcoin`
To learn more, run the command again with --verbose.
I have tried looking for examples to use this database but didn't find anything that matched. In the source code I found out that Database has a generic type, but couldn't find a way to fix my problem.
Solved! Thanks Locke and kmdreko
I am quite new to Rust, and I'm still trying to be sure I understand principles doing a small project creating a server that uses a TcpStream. The server asks for a client for a code, which has to be 8 characters long. If it's not, it shall ask again for a new code. Here is what I have so far :
fn loby_matcher(stream: &mut TcpStream) -> std::io::Result<(String, usize), >{
let mut game_code = String::new();
let mut reader = BufReader::new(stream);
let len : usize = reader.read_line(&mut game_code)?;
match len {
8 => len,
_ => return Err(Error::new(ErrorKind::Other, "wrong game code")),
};
Ok((game_code, len))
}
pub fn create_game_matcher(mut stream: TcpStream) -> std::io::Result<()>{
println!("Creating a new game! Waiting for the code.");
let game_code: String;
let len: usize;
loop {
(game_code, len) = match loby_matcher(&mut stream) {
Ok(game_code) => break (game_code, len),
Err(e) => {
stream.write("Wrong code\n".as_bytes());
(String::new(),0)
}
};
};
println!("Received : {} size {}", game_code, len);
// println!("Closing connection now I guess?");
Ok(())
}
fn start_server(address: &str, port: &str) -> std::io::Result<()>{
let listener = TcpListener::bind(format!("{}:{}", address, port))?;
for stream in listener.incoming() {
// not using ? as we do not want to stop the server for wrong connection
let stream = stream.unwrap();
thread::spawn(move || {
create_game_matcher(stream);
});
}
Ok(())
}
The loop asking for a new code when an error occurs seems wrong, but I cannot figure out a way of doing that.
Also, I know I should create a custom error in order to be able to make a difference between a wrong code and an I/O error on my TCPstream, which I will do later.
What would be the right way of looping while a specific error occurs?
Edit
If you see anything wrong in the code, do not hesitate to tell me how I could improve it.
I'm assuming you're asking how to resolve the issue in create_game_matcher. If so, then you need to move your assignment further out.
To compare the error inside io::Error, can be cumbersome. You can check the kind using e.kind() == ErrorKind::Other. However, the actual error could be anything, and doesn't require implementing PartialEq for &str. The easiest is probably just to do e.to_string() == "wrong game code".
But like you mention, the most idiomatic approach would be to create a your own error type.
pub fn create_game_matcher(mut stream: TcpStream) -> std::io::Result<()> {
println!("Creating a new game! Waiting for the code.");
let (game_code, len) = loop {
match loby_matcher(&mut stream) {
Ok(game_code) => break game_code,
// Err(e) if e.kind() == ErrorKind::Other && e.to_string() == "wrong game code" => {
// or just (for ease of use)
Err(e) if e.to_string() == "wrong game code" => {
stream.write("Wrong code\n".as_bytes())?;
}
Err(e) => return Err(e),
};
};
println!("Received : {} size {}", game_code, len);
// println!("Closing connection now I guess?");
Ok(())
}
Since you want to loop until loby_matcher succeeds, then (String::new(), 0) can be removed. You also forgot to check whether stream.write failed, so I've added an ?.
Alternatively, given that you always break after the assignment, you could also express it like this:
pub fn create_game_matcher(mut stream: TcpStream) -> std::io::Result<()> {
println!("Creating a new game! Waiting for the code.");
let game_code: String;
let len: usize;
loop {
match loby_matcher(&mut stream) {
Ok((game_code_, len_)) => {
game_code = game_code_;
len = len_;
break;
}
Err(e) if e.to_string() == "wrong game code" => {
stream.write("Wrong code\n".as_bytes())?;
}
Err(e) => return Err(e),
};
}
println!("Received : {} size {}", game_code, len);
// println!("Closing connection now I guess?");
Ok(())
}
I'm trying to read and parse a text file in Rust. Each line is a signed integer. I'm able to do it using for line in lines iteration but I'm unable to do it with a iter().map(|l| ...) one-liner. I'm getting a
expected `&core::result::Result<collections::string::String, std::io::error::Error>`,
found `core::result::Result<_, _>`
when I try to pattern match Ok(s) => match s.parse() but I'm unable to get to the bottom of what I am doing wrong. The whole example is below. The code on the bottom is the code that is producing the error.
Can anyone tell what I am doing wrong?
use std::error::Error;
use std::fs::File;
use std::io::BufReader;
use std::io::prelude::*;
use std::path::Path;
fn main() {
// Create a path to the desired file
let path = Path::new("input/numbers.txt");
let display = path.display();
// Open the path in read-only mode, returns `io::Result<File>`
let file = match File::open(&path) {
// The `description` method of `io::Error` returns a string that describes the error
Err(why) => panic!("couldn't open {}: {}", display, Error::description(&why)),
Ok(file) => file,
};
// Collect all lines into a vector
let reader = BufReader::new(file);
let lines: Vec<_> = reader.lines().collect();
// Works.
let mut nums = vec![];
for l in lines {
println!("{:?}", l);
let num = match l {
Ok(s) => match s.parse() {
Ok(i) => i,
Err(_) => 0
},
Err(_) => 0
};
nums.push(num);
}
// Doesn't work!
let nums: Vec<i64> = lines.iter().map(|l| match l {
Ok(s) => match s.parse() {
Ok(i) => i,
Err(_) => 0
},
Err(_) => 0
});
}
Let's look at the complete error message, which points to the error for us:
<anon>:5:9: 5:14 error: mismatched types:
expected `&core::result::Result<&str, ()>`,
found `core::result::Result<_, _>`
(expected &-ptr,
found enum `core::result::Result`) [E0308]
<anon>:5 Ok(s) => match s.parse() {
^~~~~
The compiler is expecting a &Result, but found a Result, and the issue is with the Ok(s) pattern. The type of l is a reference to a Result because you are using iter - which returns an iterator of references to the items in the vector.
The shortest fix is to add a & to the pattern match for the closure variable:
fn main() {
let lines: Vec<Result<_, ()>> = vec![Ok("1"), Ok("3"), Ok("5")];
// HERE V
let nums: Vec<i64> = lines.iter().map(|&l| match l {
Ok(s) => match s.parse() {
Ok(i) => i,
Err(_) => 0
},
Err(_) => 0
}).collect();
println!("{:?}", nums)
}
I also had to add collect to go back to a Vec.
The other change you could make would be to consume the input vector using into_iter and then iterate on each value in the vector:
// HERE V~~~~
let nums: Vec<i64> = lines.into_iter().map(|l| match l {
And for good measure, you could use ok, and_then, and unwrap_or to say the same thing a bit more succinctly:
let nums: Vec<i64> = lines.into_iter().map(|l| {
l.ok().and_then(|s| s.parse().ok()).unwrap_or(0)
}).collect();