Rust File examples don't compile [duplicate] - rust

This question already has answers here:
Why do try!() and ? not compile when used in a function that doesn't return Option or Result?
(4 answers)
Closed 5 years ago.
The Rust file examples don't appear compile with Rust 1.18.0.
For example:
use std::fs::File;
use std::io::prelude::*;
fn main() {
let mut file = File::open("foo.txt")?;
let mut contents = String::new();
file.read_to_string(&mut contents)?;
assert_eq!(contents, "Hello, world!");
}
Error log:
rustc 1.18.0 (03fc9d622 2017-06-06)
error[E0277]: the trait bound `(): std::ops::Carrier` is not satisfied
--> <anon>:4:20
|
4 | let mut file = File::open("foo.txt")?;
| ----------------------
| |
| the trait `std::ops::Carrier` is not implemented for `()`
| in this macro invocation
|
= note: required by `std::ops::Carrier::from_error`
error[E0277]: the trait bound `(): std::ops::Carrier` is not satisfied
--> <anon>:6:5
|
6 | file.read_to_string(&mut contents)?;
| -----------------------------------
| |
| the trait `std::ops::Carrier` is not implemented for `()`
| in this macro invocation
|
= note: required by `std::ops::Carrier::from_error`
error: aborting due to 2 previous errors

? is a syntactic sugar that checks a Result: if the result is Err, it is returned as if. If there is no error (aka Ok), the function continue. When you type this:
fn main() {
use std::fs::File;
let _ = File::open("foo.txt")?;
}
that means:
fn main() {
use std::fs::File;
let _ = match File::open("foo.txt") {
Err(e) => return Err(e),
Ok(val) => val,
};
}
Then you understand that for now, you cannot use ? in the main, because main returns unit () and not Result. If you want this stuff to work, you can put it in a function that returns a Result and check it from main:
fn my_stuff() -> std::io::Result<()> {
use std::fs::File;
use std::io::prelude::*;
let mut file = File::open("foo.txt")?;
let mut contents = String::new();
file.read_to_string(&mut contents)?;
// do whatever you want with `contents`
Ok(())
}
fn main() {
if let Err(_) = my_stuff() {
// manage your error
}
}
PS: There is a proposition to make work ? in the main.

They do compile. They just don't compile in a main function like that. If you look at the examples, they all have a big "Run" button on them. Click that and it opens the full, unabridged example on the playpen.
The one you've used above expands to this code:
fn main() {
use std::fs::File;
use std::io::prelude::*;
fn foo() -> std::io::Result<()> {
let mut file = File::open("foo.txt")?;
let mut contents = String::new();
file.read_to_string(&mut contents)?;
assert_eq!(contents, "Hello, world!");
Ok(())
}
}
That code doesn't compile because you've put code that propagates a Result into a function (main in this case) that doesn't return a Result.

Related

Error about tokio::net::TcpStream split() method. Help me please [duplicate]

I followed the code to open a file from Rust by Example:
use std::{env, fs::File, path::Path};
fn main() {
let args: Vec<_> = env::args().collect();
let pattern = &args[1];
if let Some(a) = env::args().nth(2) {
let path = Path::new(&a);
let mut file = File::open(&path);
let mut s = String::new();
file.read_to_string(&mut s);
println!("{:?}", s);
} else {
//do something
}
}
However, I got a message like this:
error[E0599]: no method named `read_to_string` found for type `std::result::Result<std::fs::File, std::io::Error>` in the current scope
--> src/main.rs:11:14
|
11 | file.read_to_string(&mut s);
| ^^^^^^^^^^^^^^ method not found in `std::result::Result<std::fs::File, std::io::Error>`
What am I doing wrong?
Let's look at your error message:
error[E0599]: no method named `read_to_string` found for type `std::result::Result<std::fs::File, std::io::Error>` in the current scope
--> src/main.rs:11:14
|
11 | file.read_to_string(&mut s);
| ^^^^^^^^^^^^^^ method not found in `std::result::Result<std::fs::File, std::io::Error>`
The error message is pretty much what it says on the tin - the type Result does not have the method read_to_string. That's actually a method on the trait Read.
You have a Result because File::open(&path) can fail. Failure is represented with the Result type. A Result may be either an Ok, which is the success case, or an Err, the failure case.
You need to handle the failure case somehow. The easiest is to just die on failure, using expect:
let mut file = File::open(&path).expect("Unable to open");
You'll also need to bring Read into scope to have access to read_to_string:
use std::io::Read;
I'd highly recommend reading through The Rust Programming Language and working the examples. The chapter Recoverable Errors with Result will be highly relevant. I think these docs are top-notch!
If your method returns Result<String, io::Error>, you can use ? on the functions that return a Result:
fn read_username_from_file() -> Result<String, io::Error> {
let mut f = File::open("hello.txt")?;
let mut s = String::new();
f.read_to_string(&mut s)?;
Ok(s)
}
If you cannot return a Result<String, io::Error> then you have to handle error case using expect as mentioned in the accepted answer or matching on the Result and panicking:
let file = File::open(&opt_raw.config);
let file = match file {
Ok(file) => file,
Err(error) => {
panic!("Problem opening the file: {:?}", error)
}
};
For more information, please refer to Recoverable Errors with Result.

Converting a Vec<&str> to Vec<&CStr> in rust

Take a look at this function:
fn exec(cli: Vec<&str>) {
eprintln!("execing: {:?}", cli);
let args: Vec<&CStr> = cli.iter()
.map(|s| CString::new(s.as_bytes()).unwrap().as_c_str())
.collect();
execv(args[0], &args);
debug(args);
}
It takes a Vec<&str> and executes it as a command. I'm having trouble converting this to Vec<&CStr> (which is what execv needs). Compiler reports this error for the map operations:
error[E0515]: cannot return value referencing temporary value
--> src/idea.rs:141:18
|
141 | .map(|s| CString::new(s.as_bytes()).unwrap().as_c_str())
| -----------------------------------^^^^^^^^^^^
| |
| returns a value referencing data owned by the current function
| temporary value created here
How do I fix this error?
You have to collect all CString to a separated vector so you references will be valid during execv call:
use std::ffi::CString;
use std::ffi::CStr;
fn main() {
let cli = vec!["hello", "world"];
let vec: Vec<_> = cli.iter()
.map(|s| CString::new(s.as_bytes()).unwrap())
.collect();
let vec_obj: Vec<&CStr> = vec.iter().map(|c| c.as_c_str()).collect();
println!("CString:{:?}", vec);
println!("&CStr:{:?}", vec_obj);
}
https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=c440ea898abe2ed5573993923ee6b74f

reqwest::client does not have get method [duplicate]

I followed the code to open a file from Rust by Example:
use std::{env, fs::File, path::Path};
fn main() {
let args: Vec<_> = env::args().collect();
let pattern = &args[1];
if let Some(a) = env::args().nth(2) {
let path = Path::new(&a);
let mut file = File::open(&path);
let mut s = String::new();
file.read_to_string(&mut s);
println!("{:?}", s);
} else {
//do something
}
}
However, I got a message like this:
error[E0599]: no method named `read_to_string` found for type `std::result::Result<std::fs::File, std::io::Error>` in the current scope
--> src/main.rs:11:14
|
11 | file.read_to_string(&mut s);
| ^^^^^^^^^^^^^^ method not found in `std::result::Result<std::fs::File, std::io::Error>`
What am I doing wrong?
Let's look at your error message:
error[E0599]: no method named `read_to_string` found for type `std::result::Result<std::fs::File, std::io::Error>` in the current scope
--> src/main.rs:11:14
|
11 | file.read_to_string(&mut s);
| ^^^^^^^^^^^^^^ method not found in `std::result::Result<std::fs::File, std::io::Error>`
The error message is pretty much what it says on the tin - the type Result does not have the method read_to_string. That's actually a method on the trait Read.
You have a Result because File::open(&path) can fail. Failure is represented with the Result type. A Result may be either an Ok, which is the success case, or an Err, the failure case.
You need to handle the failure case somehow. The easiest is to just die on failure, using expect:
let mut file = File::open(&path).expect("Unable to open");
You'll also need to bring Read into scope to have access to read_to_string:
use std::io::Read;
I'd highly recommend reading through The Rust Programming Language and working the examples. The chapter Recoverable Errors with Result will be highly relevant. I think these docs are top-notch!
If your method returns Result<String, io::Error>, you can use ? on the functions that return a Result:
fn read_username_from_file() -> Result<String, io::Error> {
let mut f = File::open("hello.txt")?;
let mut s = String::new();
f.read_to_string(&mut s)?;
Ok(s)
}
If you cannot return a Result<String, io::Error> then you have to handle error case using expect as mentioned in the accepted answer or matching on the Result and panicking:
let file = File::open(&opt_raw.config);
let file = match file {
Ok(file) => file,
Err(error) => {
panic!("Problem opening the file: {:?}", error)
}
};
For more information, please refer to Recoverable Errors with Result.

Cannot find the position of a string within a file because the BufReader is used after it's moved

I'm trying to create a function that returns the position of a string within a file. This is my code:
use std::{
fs::File,
io::{BufRead, BufReader, Seek, SeekFrom},
};
fn find(log_file: &str, key: &str) -> Option<u64> {
let log = File::open(log_file).unwrap();
let mut reader = BufReader::new(log);
for line in reader.lines() {
let line = line.unwrap();
if line.starts_with(&format!("{},", key)) {
let bytes = line.as_bytes();
let current_offset = reader.seek(SeekFrom::Current(0)).unwrap();
return Some(current_offset - bytes.len() as u64);
}
}
None
}
playground
When I attempt to compile I get:
error[E0382]: use of moved value: `reader`
--> src/main.rs:13:34
|
9 | for line in reader.lines() {
| ------ value moved here
...
13 | let current_offset = reader.seek(SeekFrom::Current(0)).unwrap();
| ^^^^^^ value used here after move
|
= note: move occurs because `reader` has type `std::io::BufReader<std::fs::File>`, which does not implement the `Copy` trait
Is there any way I can make this work?
This question is not a duplicate of Cannot use moved BufReader after for loop with bufreader.lines() because using by_ref doesn't work either because then the compiler tells me that reader is borrowed twice mutably.
I think you should use .read_line() here. This is more efficient than calling .lines()
use std::{
fs::File,
io::{BufRead, BufReader},
};
fn find(log_file: &str, key: &str) -> Option<u64> {
let log = File::open(log_file).unwrap();
let mut reader = BufReader::new(log);
let mut line = String::new();
let mut pos = 0;
while let Ok(num) = reader.read_line(&mut line) {
if num == 0 {
break;
}
if line.starts_with(&format!("{},", key)) {
return Some(pos);
}
pos += num as u64;
line.clear();
}
None
}

How to destructure tuple struct with reference

I'm trying to use the hyper library to make some requests. The Headers::get() method returns Option<&H>, where H is a tuple struct with one field. I can use if let Some() to destructure the Option. But how do we destructure the &H? Sure I could always access the field with .0, but I'm curious if Rust has a syntax to do this.
struct s(String);
fn f(input: &s) -> &s {
input
}
fn main() {
let my_struct1 = s("a".to_owned());
let s(foo) = my_struct1;
let my_struct2 = s("b".to_owned());
let &s(bar) = f(&my_struct2); // this does not work
let baz = &my_struct2.0; // this works
}
When you try to compile this, the Rust compiler will tell you how to fix the error with a nice message:
error[E0507]: cannot move out of borrowed content
--> <anon>:11:9
|
11 | let &s(bar) = f(&my_struct2); // this does not work
| ^^^---^
| | |
| | hint: to prevent move, use `ref bar` or `ref mut bar`
| cannot move out of borrowed content
This is needed to tell the compiler that you only want a reference to the field in the struct; the default matching will perform a move and the original struct value will no longer be valid.
Let's fix the example:
struct s(String);
fn f(input: &s) -> &s {
input
}
fn main() {
let my_struct1 = s("a".to_owned());
let s(foo) = my_struct1;
let my_struct2 = s("b".to_owned());
let &s(ref bar) = f(&my_struct2);
}
Another way is to dereference first and drop the &. I think this is preferred in Rust:
struct s(String);
fn f(input: &s) -> &s {
input
}
fn main() {
let my_struct1 = s("a".to_owned());
let s(foo) = my_struct1;
let my_struct2 = s("b".to_owned());
let s(ref bar) = *f(&my_struct2);
}

Resources