I'm new to Rust, so coming in from Python. With the following Rust code,
use shellexpand;
use std::fs;
fn main() {
let folder_name = "some-name";
let full_path: &str = &shellexpand::tilde("~/{}", folder_name);
fs::create_dir_all(full_path).expect("Unable to create {} folder", folder_name);
println!("{}", full_path);
}
Compilation fails because tilde and expect require one argument but I gave two. This works for the println! macro though - and f-strings' RFC hasn't landed yet [1].
For the tilde function, you will want to use the format! macro, and take reference to that. Note also, that it's highly unlikely that it'll return an &str with a dynamic input if it does what I think it does (returns the value of ~). Hence you'll want to annotate full_path as an owned String.
Next for the expect error, you'll need to do pattern matching to panic with a custom message. Something like this:
if let Err(_) = fs::create_dir_all(full_path) {
panic!("Unable to create {} folder", folder_name);
}
Postfix macro is not what we have now, and I don't think it will be stabilized within an year since it requires some nontrivial syntactic decisions so months of community bikeshedding would be necessary. Please don't teach newcommmers such feature, it only confuse them and may give them false signal which the Rust is an incomplete language. The Rust in its current state is pretty practical language actively used by industry.
Thank you all for your help. I'm pretty sure @OptimisticPeach and some of you are moving me on in the right direction:
use shellexpand;
use std::fs;
fn main() {
let folder_name = "some-name";
let full_path = shellexpand::tilde(&format!("~/{}", folder_name)).to_string();
if let Err(_) = fs::create_dir_all(folder_name) {
panic!("Unable to create {} folder", folder_name)
}
println!("{}", full_path);
}
is what I have now, and it seems to compile successfully. More questions:
Do I still use &format! in the call to shellexpand::tilde?
Should I use .to_string() as suggested by Rust when I hit some errors while compiling? Is this good practice?
Should I specify the type of full_path to be some form of String? I'm guessing that later versions of Rust (2018 Edition maybe?) infer this automatically.
The result returned by the call to shellexpand::tilde is a growable String and not &str. I'm still not sure of the difference.
Not really a question, but because full_path never gets changed, it does not need to be a &mut reference.
Question 6: Is there a equivalent of Python's os.path.sep? I think I'm hardcoding the forward slash in the call to shellexpand::tilde, or is Rust/shellexpand intelligent enough to convert that to a backslash on Windows systems?
So you can say the following instead of that, however chances are that the Cow was already a String instead of an str:
let full_path = shellexpand::tilde(&format!("~/{}", folder_name));
if let Err(_) = fs::create_dir_all(&*folder_name) {
full_path is a Cow<str>. It may be either an &'_ str or a String.
String vs str is something that's been covered extensively. However the gist of it is that a str is semantically a set of contiguous UTF-8 encoded bytes. An &str (a reference to a str) is a pointer to those bytes of dynamic length, a property which is encoded as part of the pointer. A String is a managed str. It internally is a Vec<u8> (a Vec of bytes), which it just makes sure is UTF-8.
I don't quite follow what you mean. We never take a reference to full_path.
Windows apis have become more modern and accept /s now, alongside \s. In any case, Rust apis in my experience have always been just fine with /s and \s in Windows. Additionally, \ is a valid file name in linux, so you should probably stick to /s when possible. There is std::path::MAIN_SEPARATOR which is what you're technically looking for, however you should really be using a PathBuf to manage your paths.
use shellexpand;
use std::fs;
use std::path;
fn set_path(path: &path::Path) -> path::PathBuf {
let buf = path::PathBuf::from(path);
buf
}
fn main() {
let folder_name = "some-name";
let new_path_str = shellexpand::tilde(&format!("~/{}", folder_name));
let new_path = set_path(path::Path::new(&new_path_str));
if let Err(_) = fs::create_dir_all(&new_path) {
panic!("Unable to create {:?} folder", &new_path)
}
println!("{:?}", &new_path);
}
I tried converting to use PathBuf and ended up as far as having one last error I'm unable to solve:
error[E0277]: the trait bound `std::borrow::Cow<'_, str>: std::convert::AsRef<std::ffi::OsStr>` is not satisfied
--> src/main.rs:21:45
|
21 | let new_path = set_path(path::Path::new(&new_path_str));
| ^^^^^^^^^^^^^ the trait `std::convert::AsRef<std::ffi::OsStr>` is not implemented for `std::borrow::Cow<'_, str>`
|
= help: the following implementations were found:
<std::borrow::Cow<'_, T> as std::convert::AsRef<T>>
<std::borrow::Cow<'_, std::ffi::OsStr> as std::convert::AsRef<std::path::Path>>