Extracting the last component (basename) of a filesystem path fn basename&lt;'a&gt;(path: &amp;'a str, sep: char) -&gt; Cow&lt;'a, str&gt; {
    let pieces = path.split(sep);
    match pieces.last() {
        Some(p) =&gt; p.into(),
        None =&gt; 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(&amp;args[0], '/').into_owned();


It feels like I am working too hard.
Firstly, you should use rsplit and next rather than split and last, as it starts at the more appropriate end:

fn basename&lt;'a&gt;(path: &amp;'a str, sep: char) -&gt; Cow&lt;'a, str&gt; {
    let pieces = path.rsplit(sep);
    match pieces.next() {
        Some(p) =&gt; p.into(),
        None =&gt; 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 &amp;Path or a generic parameter implementing AsRef&lt;Path&gt; and calling .as_ref() on it; str, String, Path, PathBuf and more implement it.

You can get the base name from a &amp;Path with file_name; this admittedly produces a Option&lt;&amp;OsStr&gt;, 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.