HiveBrain v1.2.0
Get Started
← Back to all entries
patternrustModerate

Extracting the last component (basename) of a filesystem path

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
lastthepathfilesystemextractingbasenamecomponent

Problem

fn basename(path: &'a str, sep: char) -> Cow {
    let pieces = path.split(sep);
    match pieces.last() {
        Some(p) => p.into(),
        None => path.into(),
    }
}


Usage:

println!("'{}'", basename("foo", '/'));    // outputs 'foo'
println!("'{}'", basename("bob/", '/'));   // outputs ''
println!("'{}'", basename("/usr/local/bin/rustc", '/')); // outputs 'rustc'


I think the split() into a match on last() is kind of elegant.
I know there is some work needed to handle both str and String, I am not sold on the use of Cow and needing to define a lifetime for the string.

I am not sold on Cow because later on I need to extract from it.

let prog = basename(&args[0], '/').into_owned();


It feels like I am working too hard.

Solution

Firstly, you should use rsplit and next rather than split and last, as it starts at the more appropriate end:

fn basename(path: &'a str, sep: char) -> Cow {
    let pieces = path.rsplit(sep);
    match pieces.next() {
        Some(p) => p.into(),
        None => path.into(),
    }
}


Secondly, you shouldn’t be using strings for this; you should be using paths, because that’s semantically what you’re dealing with.

The easiest way to get a path tends to be to take a &Path or a generic parameter implementing AsRef and calling .as_ref() on it; str, String, Path, PathBuf and more implement it.

You can get the base name from a &Path with file_name; this admittedly produces a Option, so if you want to display the path you’d need to convert it back towards a string with e.g. .and_then(|s| s.to_str()).

Anyway, the point of this latter part is just that for something that is semantically a path, you should be handling it specially, as a rule; a path need not be Unicode. Think on it more.

Code Snippets

fn basename<'a>(path: &'a str, sep: char) -> Cow<'a, str> {
    let pieces = path.rsplit(sep);
    match pieces.next() {
        Some(p) => p.into(),
        None => path.into(),
    }
}

Context

StackExchange Code Review Q#98536, answer score: 12

Revisions (0)

No revisions yet.