Reading a File with Struct, Trait and Impl

use std::fs::File;
use std::io::{self, BufRead};
use std::path::Path;

struct Parser{
}
trait Read{
fn read_lines<P>() -> io::Result<io::Lines<io::BufReader<File>>> where P: AsRef<Path>;


}
impl  Read for Parser{
fn read_lines<P>() -> io::Result<io::Lines<io::BufReader<File>>> where P: AsRef<Path> {
let file = File::open("./prueba.txt")?;
Ok(io::BufReader::new(file).lines())
}



}
fn main() {
if let Ok(lines)= Parser::read_lines(){
    for line in lines{
        println!("{:?} intento imprimir?",line);
    }
   }
 }

and see this error error[E0282]: type annotations needed
--> src/main.rs:45:23
|
45 | if let Ok(lines)= Parser::read_lines(){
| ^^^^^^^^^^^^^^^^^^ cannot infer type for type parameter P declared on the associated function read_lines

some body can't help? am new in rust and this are my firsts projects, i understand the ownership but i don't se where i infer in type of the parameter i used the same i all the ways or no?

 #![allow(dead_code, unused_mut, unused_variables)]
use std::fs::File;
use std::io::{self, BufRead};
use std::path::Path;

struct Parser{
archivo: String,

}
trait Read{
fn read_lines

(filename: &Self) -> io::Result<io::Lines<io::BufReader>> where P:
AsRef;

}
impl  Read for Parser{
 fn read_lines<P>(filename: &Self) -> io::Result<io::Lines<io::BufReader<File>>> where P: 
AsRef<Path> {
let file = File::open(filename)?;
Ok(io::BufReader::new(file).lines())
}

}

fn main() {
let archivo = Parser {
    archivo: "./prueba.txt".to_string(),
};
if let Ok(lines)= Parser::read_lines(&archivo){
    for line in lines{
        println!("{:?} intento imprimir?",line);
    }
}

}

am trie this another code but now return this error: error[E0277]: the trait bound Parser: AsRef<Path> is not satisfied
--> src/main.rs:16:27
|
16 | let file = File::open(filename)?;
| ^^^^^^^^ the trait AsRef<Path> is not implemented for Parser
|
::: /Users/her0mx/.rustup/toolchains/stable-x86_64-apple-darwin/lib/rustlib/src/rust/library/std/src/fs.rs:335:20
|
335 | pub fn open<P: AsRef>(path: P) -> io::Result {
| ----------- required by this bound in File::open
|
= note: required because of the requirements on the impl of AsRef<Path> for &Parser

First, let's format your code so it's easier to read:

use std::fs::File;
use std::io::{self, BufRead};
use std::path::Path;

struct Parser {}
trait Read {
    fn read_lines<P>() -> io::Result<io::Lines<io::BufReader<File>>>
    where
        P: AsRef<Path>;
}
impl Read for Parser {
    fn read_lines<P>() -> io::Result<io::Lines<io::BufReader<File>>>
    where
        P: AsRef<Path>,
    {
        let file = File::open("./prueba.txt")?;
        Ok(io::BufReader::new(file).lines())
    }
}

fn main() {
    if let Ok(lines) = Parser::read_lines() {
        for line in lines {
            println!("{:?} intento imprimir?", line);
        }
    }
}

(you can do this with "Tools > Rustfmt" on play.rust-lang.org)

The actual problem is that Read::read_lines has a type parameter P, but you don't use that type parameter anywhere in the arguments or return type of the function, so the compiler has no idea what type should be used to fill it when you call Parser::read_lines().

To fix this error, you could remove the parameter P from read_lines, and maybe add it back in when you update the function to take an argument that specifies the path of the file to open (which I suspect is what this code was originally doing, before you hard-coded the path "./prueba.txt").

2 Likes

this work but don't print anything... mmmmm i return the P in the second code and return an error.

That's probably because the program is unable to open ./prueba.txt. (Your main function won't do anything if Parser::read_lines() returns an Err.) Try replacing main with this:

fn main() -> io::Result<()> {
    for line in Parser::read_lines()? {
        println!("{:?} intento imprimir?", line);
    }
    Ok(())
}

With that change, if the file can't be opened then your program will print out the error instead of ignoring it.

1 Like

this work but if you trie something like this:

use std::fs::File;
use std::io::{self, BufRead};
use std::path::Path;

struct Parser{
archivo: String,
}
trait Read{
fn read_lines<P>(filename: &Self) -> io::Result<io::Lines<io::BufReader<File>>> where P:
AsRef<Path>;


}
impl  Read for Parser{
fn read_lines<P>(filename: &Self) -> io::Result<io::Lines<io::BufReader<File>>> where P:
AsRef<Path> {
    let file = File::open(filename)?;
    Ok(io::BufReader::new(file).lines())
}



}


fn main<P>() -> io::Result<()> where P:
AsRef<Path>
{
let archivo = Parser {
    archivo: "./prueba.txt".to_string(),
  };

let lines = Parser::read_lines(&archivo);
Ok(())
}

the error is: error[E0277]: the trait bound Parser: AsRef<Path> is not satisfied
--> src/main.rs:111:31
|
111 | let file = File::open(filename)?;
| ^^^^^^^^ the trait AsRef<Path> is not implemented for Parser
|
::: /Users/her0mx/.rustup/toolchains/stable-x86_64-apple-darwin/lib/rustlib/src/rust/library/std/src/fs.rs:335:20
|
335 | pub fn open<P: AsRef>(path: P) -> io::Result {
| ----------- required by this bound in File::open
|
= note: required because of the requirements on the impl of AsRef<Path> for &Parser

The signature of read_lines should be

    fn read_lines<P>(filename: &P) -> io::Result<io::Lines<io::BufReader<File>>>
    where P: AsRef<Path>

That is, the argument should be of type &P, not &Self. Self means the type you're implementing this trait for, so Parser in this case; but you don't want to pass a Parser as the argument, you want to pass a string that can be used as a filename, which is what the type parameter P stands for here.

In the future, please post your code samples in a Markdown code block (three backticks on a line, ```, followed by your code, followed by another line of three backticks), and consider formatting it using play.rust-lang.org as well. You should also use code blocks for compiler output. See this post:

2 Likes

thanks for all your help my final code is: I hope help others i recommend read about Handle File and OpenOptios.

use std::fs::{File, OpenOptions};
use std::io::{self, BufRead, Write};
struct Parser {
    texto: String,
}
trait Read {
    fn read_lines() -> io::Result<io::Lines<io::BufReader<File>>>;
    fn write_lines(self: &mut Self) -> io::Result<()>;
}
impl Read for Parser {
    fn read_lines() -> io::Result<io::Lines<io::BufReader<File>>> {
        let file = File::open("./prueba.txt")?;
        Ok(io::BufReader::new(file).lines())
    }

    fn write_lines(self: &mut Self) -> io::Result<()> {
        let mut file: File = OpenOptions::new().append(true).open("./prueba2.txt")?;
        file.write_all(&self.texto.as_bytes())?;

        Ok(())
    }
}

fn main() -> io::Result<()> {
    if let Ok(lines) = Parser::read_lines() {
        //Llamamos a la funcion y ademas verificamos el resultado de la apertura para iniciar el proceso
        for line in lines {
            let prueba = &mut line.unwrap(); //Leeemos la linea y le damos un unwrap
            let split: &Vec<&str> = &prueba.split('@').collect(); //creamos un vector que recolecta y hace una primera division
            let split: &Vec<&str> = &split[1].split(':').collect(); //creamos un segundo split para quedarnos con el puro dominio
            let to_string: &mut String = &mut split[0].to_string(); //COnvertimos a string para usar el metodo push_str
            let to_string = &mut format!("mail.{}:{}\n", to_string, prueba); // VARIABLE DE FORMATO Damos Formato

            let mut texto = Parser {
                texto: to_string.to_string(),
            }; //Definimos el texto de nuestro struck
            Parser::write_lines(&mut texto); //Llamamos a nuestra función para escribir
        }
    }
    Ok(()) //Regresamos el tipo de dato correcto
}
1 Like

Here are some ways to improve this code:

  • You didn't incorporate my suggestion to use Parser::read_lines()?, so the program will still do nothing if opening the file fails. Sticking a ? on the call to Parser::write_lines would also be a good idea, so that you will get a visible error if writing to the file fails. See this chapter of The Rust Programming Language for more on Result and ?.

  • Instead of let split: &Vec<&str> = &prueba.split('@').collect();, just write let split: Vec<&str> = prueba.split('@').collect(); (and similarly on the following lines). If you're calling a function like split collect that gives you back an owned Vec or String or whatever, you should keep that owned value, not immediately borrow it with & or &mut.

  • Using a trait here is unnecessary complexity. You can just define your methods on Parser directly, like this:

    impl Parser {
        fn read_lines() -> io::Result<io::Lines<io::BufReader<File>>> {
            // code for `read_lines` goes here
        }
    
        fn write_lines(&mut self) -> io::Result<()> {
            // code for `write_lines` goes here
        }
    }
    

    Also I'd probably change read_lines to be a free function, like this:

    // at the top level, not inside `impl Parser`
    fn read_lines() -> io::Result<io::Lines<io::BufReader<File>>> {
        // ...
    }
    

    because it doesn't do anything with the data inside the Parser struct.

1 Like

Thanks for yours comments =) i study more and more.

1 Like