I have a function that takes AsRef<Path> as an argument and looks like this
fn test<P: AsRef<std::path::Path>>(path: P) {
path.join("13123123");
}
When I compile that, it gives me the following error
error[E0599]: no method named `join` found for type `P` in the current scope
--> src/main.rs:2:10
|
2 | path.join("13123123");
| ^^^^
Try this:
path.as_ref().join("13123123")
see:
fn main() {
let path = std::path::Path::new("./foo/bar/");
test(path);
}
fn test<P: AsRef<std::path::Path>>(path: P) {
println!("{:?}", path.as_ref().join("13123123"));
}
Output:
"./foo/bar/13123123"
See the documentation for AsRef.
Related
I am new to rust and trying to figure out what I am doing wrong here. From the docs.rs page on walkdir:
The following code recursively iterates over the directory given and prints the path for each entry:
use walkdir::WalkDir;
for entry in WalkDir::new("foo") {
println!("{}", entry?.path().display());
}
However when I try to run this as a simple program:
use walkdir::WalkDir;
fn main() {
for entry in WalkDir::new("~/Documents/ExampleDir/") {
println!("{}", entry?.path().display());
}
}
I get a compilation error:
error[E0277]: the `?` operator can only be used in a function that returns `Result` or `Option` (or another type that implements `Try`)
--> src/main.rs:4:24
|
2 | / fn main() {
3 | | for entry in WalkDir::new("foo") {
4 | | println!("{}", entry?.path().display());
| | ^^^^^^ cannot use the `?` operator in a function that returns `()`
5 | | }
6 | | }
| |_- this function should return `Result` or `Option` to accept `?`
|
= help: the trait `Try` is not implemented for `()`
= note: required by `from_error`
If I remove the ? operator then I get a different compilation error:
error[E0599]: no method named `path` found for enum `std::result::Result<walkdir::DirEntry, walkdir::Error>` in the current scope
--> src/main.rs:4:30
|
4 | println!("{}", entry.path().display());
| ^^^^ method not found in `std::result::Result<walkdir::DirEntry, walkdir::Error>`
What am I doing wrong here? Is this an issue in my code or in the documentation?
The ? is shorthand for a match statement. See The question mark operator.
The question mark operator (?) unwraps valid values or returns erroneous values, propagating them to the calling function. It is a unary postfix operator that can only be applied to the types Result<T, E> and Option.
The (main) function requires a return value of type Result:
use std::error::Error;
use walkdir::WalkDir;
fn main() -> Result<(), Box<dyn Error>> {
for entry in WalkDir::new("~/Documents/ExampleDir/") {
println!("{}", entry?.path().display());
}
Ok(())
}
I'm building a wrapper around a DLL. This DLL gives me access to a database engine which implements an OOP design pattern. This requires me to create multiple overlapping traits that cover all the functionality:
pub trait CursorStatement { /* ... */ }
pub trait CursorTable { /* ... */ }
pub trait CursorStatementTable { /* ... */ }
...
I want to be able to bring these traits in scope so that I can call the functions without having to list every trait. Right now I'm doing:
mod traittest;
use traittest::*;
fn test() -> Result<(), AceError> {
let t = traittest::Table::new(3, "ORDERS")?;
let c = traittest::Cursor { handle: 42 };
println!("t.fields={}", t.fields());
println!("c.fields={}", c.fields());
Ok(())
}
fn main() {
test().expect("success");
}
The problem with use foo::* is that it puts everything from the module into my namespace, which I don't want.
In the example above, I don't have to type traittest::Table or traittest::Cursor, I just have to type Table or Cursor. However, I want to have to prefix those objects with the module name so when I'm reading the code I know where the objects came from. I might want to create a Table object in my local file that is distinguished from the one coming from the module.
I also don't want to have to do the following because if I later have to add a new trait I will have to update a bunch of other source files that depend on this module:
mod traittest;
use traittest::{CursorStatement, CursorStatementTable, CursorTable, /* ... */};
I tried creating a Traits supertrait that would inherit all other traits as shown in Is there any way to create a type alias for multiple traits?, but it doesn't work because I can't implement the trait for anything because there's nothing that would be an implementation of every trait in the file:
pub trait Traits: CursorStatement, CursorTable, CursorStatementHandle, /* ... */ {}
If I could create a named scope for all the traits, that would work, but I can't figure out how to make Rust happy with this idea:
let traits = {
pub trait CursorTable { /* ... */ }
}
It looks like this trait_group macro might do the trick but it's not obvious to me how I could use it to solve my problem.
Here's my entire program
mod traittest {
#[derive(Debug)]
pub struct AceError {
code: u32,
description: String,
}
pub trait CursorTable {
fn get_handle(&self) -> u32; // impl's must write this function
fn fields(&self) -> String {
return format!("(get_handle() -> {})", self.get_handle());
}
}
pub struct Table {
pub handle: u32,
pub table_name: String,
}
pub struct Cursor {
pub handle: u32,
}
impl Table {
pub fn new(handle: u32, table_name: &str) -> Result<Table, AceError> {
let table = Table {
handle: handle,
table_name: table_name.to_string(),
};
return Ok(table);
}
}
impl CursorTable for Table {
fn get_handle(&self) -> u32 {
return self.handle;
}
}
impl CursorTable for Cursor {
fn get_handle(&self) -> u32 {
return self.handle;
}
}
pub trait Traits: CursorTable {} /* super trait to bring all other traits in scope */
}
use traittest::Traits;
fn test() -> Result<(), traittest::AceError> {
let t = traittest::Table::new(3, "ORDERS")?;
let c = traittest::Cursor { handle: 42 };
println!("t.fields={}", t.fields());
println!("c.fields={}", c.fields());
Ok(())
}
fn main() {
test().expect("success");
}
and here's the error I get:
warning: unused import: `traittest::Traits`
--> src/main.rs:49:5
|
49 | use traittest::Traits;
| ^^^^^^^^^^^^^^^^^
|
= note: `#[warn(unused_imports)]` on by default
error[E0599]: no method named `fields` found for struct `traittest::Table` in the current scope
--> src/main.rs:54:31
|
10 | fn fields(&self) -> String {
| ------
| |
| the method is available for `std::boxed::Box<traittest::Table>` here
| the method is available for `std::sync::Arc<traittest::Table>` here
| the method is available for `std::rc::Rc<traittest::Table>` here
...
15 | pub struct Table {
| ---------------- method `fields` not found for this
...
54 | println!("t.fields={}", t.fields());
| ^^^^^^ method not found in `traittest::Table`
|
= help: items from traits can only be used if the trait is in scope
help: the following trait is implemented but not in scope; perhaps add a `use` for it:
|
49 | use crate::traittest::CursorTable;
|
error[E0599]: no method named `fields` found for struct `traittest::Cursor` in the current scope
--> src/main.rs:55:31
|
10 | fn fields(&self) -> String {
| ------
| |
| the method is available for `std::boxed::Box<traittest::Cursor>` here
| the method is available for `std::sync::Arc<traittest::Cursor>` here
| the method is available for `std::rc::Rc<traittest::Cursor>` here
...
20 | pub struct Cursor {
| ----------------- method `fields` not found for this
...
55 | println!("c.fields={}", c.fields());
| ^^^^^^ method not found in `traittest::Cursor`
|
= help: items from traits can only be used if the trait is in scope
help: the following trait is implemented but not in scope; perhaps add a `use` for it:
|
49 | use crate::traittest::CursorTable;
|
I finally figured out a solution I can live with, in case anybody else is looking for a solution to this problem.
In your module where you define your traits, create a sub-module with all the traits, like so:
pub mod Traits
{
pub trait CursorTrait
{
fn get_handle ( &self ) -> u32; // impl's must write this function
fn fields ( &self ) -> String
{
return format! ( "(get_handle() -> {})", self.get_handle() );
}
}
}
Now in your other modules, if you want to bring the traits in scope without bringing in the entire module, you can just bring in the submodule, like so:
mod foo; use foo::Traits::*;
I am pretty sure a code like this worked before:
fn f<F: fn()>(f: F) {
}
However, now it gives the following error:
error: expected one of `(`, `,`, `=`, `>`, `?`, `for`, lifetime, or path, found `fn`
--> src/main.rs:96:13
|
96 | fn f<F: fn()>(f: F) {
| -^^ unexpected token
| |
| expected one of 8 possible tokens here
Note that this compiles fine:
fn f(f: fn()) {
}
You must use the function trait:
fn f<F: Fn()>(f: F) {
}
fn is a function pointer, not a trait.
Note that you can also write this:
fn f(f: fn()) {
}
This question already has answers here:
Returning a closure from a function
(4 answers)
Closed 5 years ago.
I wrote the following Rust program to print out only command-line arguments that are integers. It works perfectly:
use std::env;
fn main() {
for i in env::args().filter_map(|arg| arg.parse::<i32>().ok()) {
println!("{}", i);
}
}
I then attempted to re-write the program to abstract the filter into a function. This version does not compile.
use std::env::Args;
use std::env;
use std::iter::FilterMap;
// Version 2
fn main() {
for i in nums(&env::args()) {
println!("{}", i);
}
}
fn nums<F: Fn(String) -> Option<i32>>(args: &Args) -> FilterMap<Args,F> {
args.filter_map(|arg| arg.parse::<i32>().ok())
}
It produces the following compilation errors:
Compiling iterator_return_type v0.1.0 (file:///Users/gabriel/AllProjects/SentimentAnalysis/iterator_return_type)
error[E0282]: type annotations needed
--> src/main.rs:16:9
|
16 | for i in nums(&env::args()) {
| ^ cannot infer type for `_`
error: the type of this value must be known in this context
--> src/main.rs:22:27
|
22 | args.filter_map(|arg| arg.parse::<i32>().ok())
| ^^^^^^^^^^^^^^^^^^
error[E0308]: mismatched types
--> src/main.rs:22:21
|
22 | args.filter_map(|arg| arg.parse::<i32>().ok())
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected type parameter, found closure
|
= note: expected type `F`
found type `[closure#src/main.rs:22:21: 22:50]`
error: aborting due to previous error(s)
error: Could not compile `iterator_return_type`.
What I find particularly confusing is the final compilation error. I do not understand how else I might specify a closure type.
Thanks!
impl Trait and Box<Trait> solution can be applied to both iteratorsand closures, both of them are just traits! The difference is you have to use them in the closure case.
If you want to use impl Trait, then your code will look like this (note that Args should be passed by value):
#![feature(conservative_impl_trait)]
use std::env::Args;
use std::env;
use std::iter::FilterMap;
fn main() {
for i in nums(env::args()) {
println!("{}", i);
}
}
fn nums(args: Args) -> FilterMap<Args, impl FnMut(String) -> Option<i32>> {
args.filter_map(|arg| arg.parse::<i32>().ok())
}
However, you usually need not expose the detail of the iterator type; therefore you can do it like this way:
fn nums(args: Args) -> impl Iterator<Item = i32> {
args.filter_map(|arg| arg.parse::<i32>().ok())
}
What if you want to use stable Rust? Unfortunately you'll have to use boxing for now.
fn nums(args: Args) -> Box<Iterator<Item = i32>> {
Box::new(args.filter_map(|arg| arg.parse::<i32>().ok()))
}
Why can't you describe a full type of closures, despite that you can describe an iterator like Zip<Drain<'a, i32>, IntoIter<&'b str>>? There are two reasons:
Closure types are anonymous by nature; you'll have to anonymize (impl Fn()) or box (Box<Fn()>) them if you want to return them.
The interface for closure traits is unstable; you can't implement them (impl Fn() for YourType { .. }) stably.
Then why doesn't your code work? The reason is:
If you want to pass closures to a function, the caller decides its type. In this case you can write fn foo<T: Fn()>() { .. }.
If you want to pass closures from a function, the callee decides its type. In this case you'll have to use impl Trait.
RFC 1951 will change this distinction. You will be able to use impl Trait in both cases.
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