The choice seems to be between std::fs::PathExt and std::fs::metadata, but the latter is suggested for the time being as it is more stable. Below is the code I have been working with as it is based off the docs:
use std::fs;
pub fn path_exists(path: &str) -> bool {
let metadata = try!(fs::metadata(path));
assert!(metadata.is_file());
}
However, for some odd reason let metadata = try!(fs::metadata(path)) still requires the function to return a Result<T,E> even though I simply want to return a boolean as shown from assert!(metadata.is_file()).
Even though there will probably be a lot of changes to this soon enough, how would I bypass the try!() issue?
Below is the relevant compiler error:
error[E0308]: mismatched types
--> src/main.rs:4:20
|
4 | let metadata = try!(fs::metadata(path));
| ^^^^^^^^^^^^^^^^^^^^^^^^ expected bool, found enum `std::result::Result`
|
= note: expected type `bool`
found type `std::result::Result<_, _>`
= note: this error originates in a macro outside of the current crate
error[E0308]: mismatched types
--> src/main.rs:3:40
|
3 | pub fn path_exists(path: &str) -> bool {
| ________________________________________^
4 | | let metadata = try!(fs::metadata(path));
5 | | assert!(metadata.is_file());
6 | | }
| |_^ expected (), found bool
|
= note: expected type `()`
found type `bool`
Note that many times you want to do something with the file, like read it. In those cases, it makes more sense to just try to open it and deal with the Result. This eliminates a race condition between "check to see if file exists" and "open file if it exists". If all you really care about is if it exists...
Rust 1.5+
Path::exists... exists:
use std::path::Path;
fn main() {
println!("{}", Path::new("/etc/hosts").exists());
}
As mental points out, Path::exists simply calls fs::metadata for you:
pub fn exists(&self) -> bool {
fs::metadata(self).is_ok()
}
Rust 1.0+
You can check if the fs::metadata method succeeds:
use std::fs;
pub fn path_exists(path: &str) -> bool {
fs::metadata(path).is_ok()
}
fn main() {
println!("{}", path_exists("/etc/hosts"));
}
You can use std::path::Path::is_file:
use std::path::Path;
fn main() {
let b = Path::new("file.txt").is_file();
println!("{}", b);
}
https://doc.rust-lang.org/std/path/struct.Path.html#method.is_file
Related
I thought I'd try to write a basic function decorator that logs the input and output of functions as they are called. I have many functions that have roughly the same signature, and I'd like to log the io of common part. That is, think of adding a decorator like this
#[my_decorator]
foo(<unknown initial args>, final_arg: T) -> R {BODY}
would effectively turn foo into
foo(<unknown initial args>, final_arg: T) -> R {
let result = {BODY};
debug!("final_arg is {:?}" final_arg)
debug!("return value is {:?}" result)
result
}
I spent today searching around for best approaches to this and it appears that the best way would be to define a #[proc_macro_attribute]
I am at the following sanity-checking stub (which the documentation here would suggest is valid)
use proc_macro::TokenStream;
use syn;
use syn::ItemFn;
#[proc_macro_attribute]
pub fn log_io(_attr: TokenStream, item: TokenStream) -> TokenStream {
let item_fn: ItemFn = syn::parse_macro_input!(item);
item_fn.to_token_stream()
}
Which IIUC should be the No-Op decorator. But the compiler gives
error[E0599]: no method named `to_token_stream` found for struct `ItemFn` in the current scope
--> src/lib.rs:8:13
|
8 | item_fn.to_token_stream()
| ^^^^^^^^^^^^^^^ method not found in `ItemFn`
|
::: /Users/arthurtilley/.cargo/registry/src/github.com-1ecc6299db9ec823/quote-1.0.21/src/to_tokens.rs:59:8
|
59 | fn to_token_stream(&self) -> TokenStream {
| --------------- the method is available for `ItemFn` here
|
= help: items from traits can only be used if the trait is in scope
help: one of the expressions' fields has a method of the same name
|
8 | item_fn.sig.to_token_stream()
| ++++
help: the following trait is implemented but not in scope; perhaps add a `use` for it:
|
1 | use syn::__private::ToTokens;
|
Adding the suspicious looking use syn::__private::ToTokens; the compiler then gives
error[E0308]: mismatched types
--> src/lib.rs:9:5
|
7 | pub fn log_io(_attr: TokenStream, item: TokenStream) -> TokenStream {
| ----------- expected `proc_macro::TokenStream` because of return type
8 | let item_fn: ItemFn = syn::parse_macro_input!(item);
9 | item_fn.to_token_stream()
| ^^^^^^^^^^^^^^^^^^^^^^^^^ expected struct `proc_macro::TokenStream`, found struct `TokenStream2`
None of this is mentioned in the documentation https://docs.rs/syn/latest/syn/struct.ItemFn.html so I'm a little stumped.
EDIT: #Shelvacu I'm using
syn = {version ="*" ("1.0.99"), features = ["full", "printing", "extra-traits"]}
proc-macro2 = "*" ("1.0.43")
If you click on TokenStream in fn to_token_stream you'll notice that it returns proc_macro2::TokenStream which is different from proc_macro::TokenStream that your function is defined to return. You can read more about this in the proc-macro2 docs. The way to convert proc_macro2::TokenStream to proc_macro::TokenStream as recommended in the docs is:
proc_macro::TokenStream::from(output)
In your case, this should work:
#[proc_macro_attribute]
pub fn log_io(_attr: TokenStream, item: TokenStream) -> TokenStream {
let item_fn: ItemFn = syn::parse_macro_input!(item);
proc_macro::TokenStream::from(item_fn.to_token_stream());
}
Also, you should import ToTokens trait from quote::ToTokens (from the quote crate) as the syn re-export is marked private.
mystring.rs
pub fn return_string() {
return "Some String"
}
then in main, I want to print this string
mod mystring;
const test = config::return_string();
println!("{}", test);
the error I get is
println!("{}", test);
| ^^^^ `()` cannot be formatted with the default formatted
I assume that your minimal reproducible example is:
pub fn return_string() {
return "Some String"
}
fn main() {
const test = return_string();
println!("{}", test);
}
error: missing type for `const` item
--> src/main.rs:6:11
|
6 | const test = return_string();
| ^^^^ help: provide a type for the constant: `test: ()`
error[E0308]: mismatched types
--> src/main.rs:2:12
|
1 | pub fn return_string() {
| - help: try adding a return type: `-> &'static str`
2 | return "Some String"
| ^^^^^^^^^^^^^ expected `()`, found `&str`
error[E0277]: `()` doesn't implement `std::fmt::Display`
--> src/main.rs:7:20
|
7 | println!("{}", test);
| ^^^^ `()` cannot be formatted with the default formatter
|
= help: the trait `std::fmt::Display` is not implemented for `()`
= note: in format strings you may be able to use `{:?}` (or {:#?} for pretty-print) instead
= note: this error originates in the macro `$crate::format_args_nl` (in Nightly builds, run with -Z macro-backtrace for more info)
Solution
There are two errors in your code:
don't use const inside of functions. Use let. let is an immutable value. let mut would be mutable. const is only used for immutable globals.
you are missing the return type for your return_string() function
I'll assume here that the return type is &str, but it might as well have to be String. For more info search for &str vs String.
Third, as a minor annotation, avoid return as much as possible, if not required. The last line of a function is automatically the return type if you don't finish it with ;.
pub fn return_string() -> &'static str {
"Some String"
}
fn main() {
let test = return_string();
println!("{}", test);
}
Some String
Explanation of the error message
The error message says that () is not printable.
() is the empty type, analogous to void in C++. As you don't annotate the return type of return_string(), Rust assumes it's (). And () cannot be printed directly, at least not with the Display formatter.
You could print it with the Debug formatter, though:
pub fn return_void() {}
fn main() {
let test = return_void();
println!("{:?}", test);
}
()
Note that contrary to C++, () is actually a storable type, even if it is of size 0 with no data in it. That makes things a lot easier for generics. C++ templates that need to be able to deal with void return values were a major pain factor for me in the past, as they always required a special case.
This is the code I am trying to execute:
fn my_fn(arg1: &Option<Box<i32>>) -> i32 {
if arg1.is_none() {
return 0;
}
let integer = arg1.unwrap();
*integer
}
fn main() {
let integer = 42;
my_fn(&Some(Box::new(integer)));
}
(on the Rust playground)
I get the following error in previous versions of Rust:
error[E0507]: cannot move out of borrowed content
--> src/main.rs:5:19
|
5 | let integer = arg1.unwrap();
| ^^^^ cannot move out of borrowed content
And in more modern versions:
error[E0507]: cannot move out of `*arg1` which is behind a shared reference
--> src/main.rs:5:19
|
5 | let integer = arg1.unwrap();
| ^^^^
| |
| move occurs because `*arg1` has type `std::option::Option<std::boxed::Box<i32>>`, which does not implement the `Copy` trait
| help: consider borrowing the `Option`'s content: `arg1.as_ref()`
I see there is already a lot of documentation about borrow checker issues, but after reading it, I still can't figure out the problem.
Why is this an error and how do I solve it?
Option::unwrap() consumes the option, that is, it accepts the option by value. However, you don't have a value, you only have a reference to it. That's what the error is about.
Your code should idiomatically be written like this:
fn my_fn(arg1: &Option<Box<i32>>) -> i32 {
match arg1 {
Some(b) => **b,
None => 0,
}
}
fn main() {
let integer = 42;
my_fn(&Some(Box::new(integer)));
}
(on the Rust playground)
Or you can use Option combinators like Option::as_ref or Option::as_mut paired with Option::map_or, as Shepmaster has suggested:
fn my_fn(arg1: &Option<Box<i32>>) -> i32 {
arg1.as_ref().map_or(0, |n| **n)
}
This code uses the fact that i32 is automatically copyable. If the type inside the Box weren't Copy, then you wouldn't be able to obtain the inner value by value at all - you would only be able to clone it or to return a reference, for example, like here:
fn my_fn2(arg1: &Option<Box<i32>>) -> &i32 {
arg1.as_ref().map_or(&0, |n| n)
}
Since you only have an immutable reference to the option, you can only return an immutable reference to its contents. Rust is smart enough to promote the literal 0 into a static value to keep in order to be able to return it in case of absence of the input value.
Since Rust 1.40 there is Option::as_deref, so now you can do:
fn my_fn(arg1: &Option<Box<i32>>) -> i32 {
*arg1.as_deref().unwrap_or(&0)
}
In the code below, I'm trying to pass an Option<FnOnce(&mut Thing)> to the higher order function invoke_me_maybe(). The function being passed will be invoked if it is present, and not invoked otherwise.
The Option<FnOnce(&mut Thing)> are constructed using as_some() from additional trait methods on booleans, copied the boolinator crate.
struct Thing{}
fn invoke_me_maybe<F: FnOnce(&mut Thing)>(t: &mut Thing, opt_f: Option<F>) {
if let Some(f) = opt_f {
f(t);
}
}
trait BoolOption {
fn as_some<T>(self, some: T) -> Option<T>;
}
impl BoolOption for bool {
fn as_some<T>(self, some: T) -> Option<T> {
if self { Some(some) } else { None }
}
}
pub fn main() {
let mut thing = Thing{};
invoke_me_maybe(&mut thing, true.as_some(|t| {}));
}
The invoke_me_maybe() function does not keep opt_f beyond the end of the function, so we should not need to wrap the function in a Box or anything like that.
The error produced is as follows:
error[E0631]: type mismatch in closure arguments
--> src/main.rs:21:33
|
3 | fn invoke_me_maybe<F: FnOnce(&mut Thing)>(t: &mut Thing, opt_f: Option<F>) {
| --------------- ------------------ required by this bound in `invoke_me_maybe`
...
21 | invoke_me_maybe(&mut thing, true.as_some(|t| {}));
| ^^^^^^^^^^^^^---^^^^
| | |
| | found signature of `fn(_) -> _`
| expected signature of `for<'r> fn(&'r mut Thing) -> _`
error[E0271]: type mismatch resolving `for<'r> <[closure#src/main.rs:21:46: 21:52] as std::ops::FnOnce<(&'r mut Thing,)>>::Output == ()`
--> src/main.rs:21:5
|
3 | fn invoke_me_maybe<F: FnOnce(&mut Thing)>(t: &mut Thing, opt_f: Option<F>) {
| --------------- ------------------ required by this bound in `invoke_me_maybe`
...
21 | invoke_me_maybe(&mut thing, true.as_some(|t| {}));
| ^^^^^^^^^^^^^^^ expected bound lifetime parameter, found concrete lifetime
error: aborting due to 2 previous errors
Some errors have detailed explanations: E0271, E0631.
For more information about an error, try `rustc --explain E0271`.
error: could not compile `playground`.
To learn more, run the command again with --verbose.
I'm probably missing some explicit lifetime parameters or something like that, but I can't figure it out. Doesn't fn(_) -> _ already match with for<'r> fn(&'r mut Thing) -> _?
Using closures as higher order functions is tricky. I've noticed that explicitly writing the type of the arguments usually helps. In your case it does the trick:
invoke_me_maybe(&mut thing, true.as_some(|t: &mut Thing| {}));
The problem seems to be that invoke_me_maybe takes a generic argument with a lot of possibilities, while |t| {} may mean anything, and the compiler is not able to match both. Adding the type annotation helps in this case.
Personally I consider this a compiler bug, but I've been wrong before...
This is the code I am trying to execute:
fn my_fn(arg1: &Option<Box<i32>>) -> i32 {
if arg1.is_none() {
return 0;
}
let integer = arg1.unwrap();
*integer
}
fn main() {
let integer = 42;
my_fn(&Some(Box::new(integer)));
}
(on the Rust playground)
I get the following error in previous versions of Rust:
error[E0507]: cannot move out of borrowed content
--> src/main.rs:5:19
|
5 | let integer = arg1.unwrap();
| ^^^^ cannot move out of borrowed content
And in more modern versions:
error[E0507]: cannot move out of `*arg1` which is behind a shared reference
--> src/main.rs:5:19
|
5 | let integer = arg1.unwrap();
| ^^^^
| |
| move occurs because `*arg1` has type `std::option::Option<std::boxed::Box<i32>>`, which does not implement the `Copy` trait
| help: consider borrowing the `Option`'s content: `arg1.as_ref()`
I see there is already a lot of documentation about borrow checker issues, but after reading it, I still can't figure out the problem.
Why is this an error and how do I solve it?
Option::unwrap() consumes the option, that is, it accepts the option by value. However, you don't have a value, you only have a reference to it. That's what the error is about.
Your code should idiomatically be written like this:
fn my_fn(arg1: &Option<Box<i32>>) -> i32 {
match arg1 {
Some(b) => **b,
None => 0,
}
}
fn main() {
let integer = 42;
my_fn(&Some(Box::new(integer)));
}
(on the Rust playground)
Or you can use Option combinators like Option::as_ref or Option::as_mut paired with Option::map_or, as Shepmaster has suggested:
fn my_fn(arg1: &Option<Box<i32>>) -> i32 {
arg1.as_ref().map_or(0, |n| **n)
}
This code uses the fact that i32 is automatically copyable. If the type inside the Box weren't Copy, then you wouldn't be able to obtain the inner value by value at all - you would only be able to clone it or to return a reference, for example, like here:
fn my_fn2(arg1: &Option<Box<i32>>) -> &i32 {
arg1.as_ref().map_or(&0, |n| n)
}
Since you only have an immutable reference to the option, you can only return an immutable reference to its contents. Rust is smart enough to promote the literal 0 into a static value to keep in order to be able to return it in case of absence of the input value.
Since Rust 1.40 there is Option::as_deref, so now you can do:
fn my_fn(arg1: &Option<Box<i32>>) -> i32 {
*arg1.as_deref().unwrap_or(&0)
}