This is my first module I wrote in Rust. I'm new to the language and couldn't find a way to easily replace some words in a file, so that's my go at it. If there's already something like that and I just missed it, feel free to point me to it.
I'm looking for advice as to how I can make this more efficient and more "rustician" "Rustacean", so I can publish a crate with this for others to use.
Since I have to call "string".to_string()
all the time, &str
would be easier for this case here I guess, but would it be better? When should I generally use String
and when &str
?
main.rs
mod str_replace;
use str_replace::StrReplace;
fn main()
{
let mut str_test = StrReplace::from_str("hello 1 2 1 2 hello bye");
str_test.replace("hello".to_string(), "bye".to_string())
.replace("1".to_string(), "0".to_string())
.replace("2".to_string(), "1".to_string());
println!("{:?}", *str_test);
let mut file_test = StrReplace::from_file("src/test.txt");
file_test.replace("hello".to_string(), "bye".to_string())
.replace("1".to_string(), "0".to_string())
.replace("2".to_string(), "1".to_string())
.replace("\n".to_string(), " ".to_string());
println!("{:?}", file_test.to_str());
file_test.to_file("src/to_file_test.txt");
let replace_here_test = StrReplace::replace_here("hello".to_string(), "bye".to_string(), "hello hello".to_string());
println!("{:?}", replace_here_test);
}
str_replace.rs
use std::path::Path;
use std::fs::File;
use std::io::prelude::{Read, Write};
use std::ops::Deref;
#[derive(Debug)]
pub struct StrReplace
{
/// String or &str?
data: String,
}
impl StrReplace
{
///
/// Creates StrReplace from the contents of a file at the given path
///
/// Tried to wrap the "try!" macro around "file.read_to_string",
/// like in the documentation, but I got an error:
///
/// error[E0308]: mismatched types
/// --> src/str_replace.rs:37:9
/// |
/// 37 | try!(file.read_to_string(&mut data));
/// | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected struct `str_replace::StrReplace`, found enum `std::result::Result`
/// |
/// = note: expected type `str_replace::StrReplace`
/// = note: found type `std::result::Result<_, _>`
/// = note: this error originates in a macro outside of the current crate
///
pub fn from_file(path: &str) -> StrReplace
{
let filepath = Path::new(path);
let mut file = File::open(filepath).unwrap();
let mut data = String::new();
file.read_to_string(&mut data).expect("Failed to read file.");
//try!(file.read_to_string(&mut data));
StrReplace { data: data }
}
///
/// Creates StrReplace from a given &str
///
/// or maybe from a String?
///
pub fn from_str(str: &str) -> StrReplace
{
StrReplace { data: str.to_string() }
}
///
/// Replace the occurence of one string with another
/// uses the std String.replace function
/// returns self for chainability.
///
/// any downsides to chaining this way?
/// function arguments String or &str?
///
pub fn replace(&mut self, search: String, replacement: String) -> &mut Self
{
self.data = self.data.replace(&*search, &*replacement);
self
}
///
/// Writes the possibly mutated data to a file at the given destination
///
/// Again, should dst be &str or String?
/// Also when trying to use try! here I get an mismatched types error again,
/// but this time with an other expected type:
///
/// error[E0308]: mismatched types
/// --> src/str_replace.rs:88:9
/// |
/// 88 | try!(file.write_all(self.data.as_bytes()));
/// | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected (), found enum `std::result::Result`
/// |
/// = note: expected type `()`
/// = note: found type `std::result::Result<_, _>`
/// = note: this error originates in a macro outside of the current crate
///
pub fn to_file(&self, dst: &str)
{
let mut file = File::create(dst).unwrap();
file.write_all(self.data.as_bytes()).expect("Failed to write file.");
//try!(file.write_all(self.data.as_bytes()));
}
///
/// Makes a &str out of StrReplace for further use
///
/// Better to return &str or String?
///
pub fn to_str(&self) -> &str
{
&*self.data
}
///
/// Replace the occurence of one string with another
/// without creating a StrReplace
///
/// Again, String or &str?
///
pub fn replace_here(search: String, replacement: String, input: String) -> String
{
input.replace(&*search, &*replacement)
}
}
/// Does it make sense to implement this? I guess yes
/// But when would it not make sense?
impl Deref for StrReplace
{
/// String or &str?
/// &str didn't work for me yet because of the lifetime thingy I have yet to fully understand
type Target = String;
fn deref(&self) -> &Self::Target
{
&self.data
}
}