File Paths in Rust

Here’s the translation of the Go code to Rust, formatted in Markdown suitable for Hugo:

The std::path module provides functions to parse and construct file paths in a way that is portable between operating systems; dir/file on Linux vs. dir\file on Windows, for example.

use std::path::Path;

fn main() {
    // `join` should be used to construct paths in a
    // portable way. It takes any number of arguments
    // and constructs a hierarchical path from them.
    let p = Path::new("dir1").join("dir2").join("filename");
    println!("p: {:?}", p);

    // You should always use `join` instead of
    // concatenating `/`s or `\`s manually. In addition
    // to providing portability, `join` will also
    // normalize paths by removing superfluous separators
    // and directory changes.
    println!("{:?}", Path::new("dir1//").join("filename"));
    println!("{:?}", Path::new("dir1/../dir1").join("filename"));

    // `parent` and `file_name` can be used to split a path to the
    // directory and the file. 
    println!("parent(p): {:?}", p.parent());
    println!("file_name(p): {:?}", p.file_name());

    // We can check whether a path is absolute.
    println!("{}", Path::new("dir/file").is_absolute());
    println!("{}", Path::new("/dir/file").is_absolute());

    let filename = "config.json";

    // Some file names have extensions following a dot. We
    // can split the extension out of such names with `extension`.
    let ext = Path::new(filename).extension();
    println!("{:?}", ext);

    // To find the file's name with the extension removed,
    // use `file_stem`.
    println!("{:?}", Path::new(filename).file_stem());

    // `strip_prefix` finds a relative path between a *base* and a
    // *target*. It returns an error if the target cannot
    // be made relative to base.
    match Path::new("a/b/t/file").strip_prefix("a/b") {
        Ok(rel) => println!("{:?}", rel),
        Err(e) => panic!("{}", e),
    }

    match Path::new("a/c/t/file").strip_prefix("a/b") {
        Ok(rel) => println!("{:?}", rel),
        Err(e) => panic!("{}", e),
    }
}

To run the program, save it as file_paths.rs and use rustc to compile it:

$ rustc file_paths.rs
$ ./file_paths
p: "dir1/dir2/filename"
"dir1/filename"
"dir1/filename"
parent(p): Some("dir1/dir2")
file_name(p): Some("filename")
false
true
Some("json")
Some("config")
"t/file"
thread 'main' panicked at 'prefix not found', src/main.rs:38:23
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace

In this Rust version:

  • We use the std::path::Path struct instead of the filepath package.
  • Join is replaced by chaining join method calls.
  • Dir and Base are replaced by parent and file_name methods.
  • IsAbs is replaced by the is_absolute method.
  • Ext is replaced by the extension method.
  • strings.TrimSuffix is replaced by the file_stem method.
  • Rel is replaced by strip_prefix.

Note that Rust’s path operations return Option or Result types, which we handle appropriately in the example.