Create a path

In this tutorial you will construct and compare Paths using the Path API.

Prerequisites

A basic knowledge of the Rust programming language and executing commands in the terminal will be helpful for completing this tutorial. Some of the steps below also require cargo to be installed.

Setup

  1. Create a new directory on your filesystem and name it something like path.
  2. Using your terminal, run cargo init within the newly created directory.
  3. After than, run cargo add willow25.

Construct an empty path

We'll now create an empty Path using Path::new.

Open src/main.rs, delete its contents, and enter the following:

use willow25::prelude::*;

fn main() {
    let empty_path = Path::new();
    println!("A path with nothing in it: {:?}", empty_path);
}

In your terminal, run cargo run, and you should see the following output:

A path with nothing in it: Path(<empty>)

Append components to a path

We'll now create two Components and append them to a Path using Path::append_component.

Add the following code to src/main.rs:

use willow25::prelude::*;

fn main() {
    let empty_path = Path::new();
    println!("A path with nothing in it: {:?}", empty_path);
    
    let path = Path::new();
    let component1 = component!("ideas");
    let component2 = component!("game");
    let ideas_path = path.append_component(component1).unwrap();
    let ideas_game_path = ideas_path.append_component(component2).unwrap();
    println!("A path with two components: {:?}", ideas_game_path);
}

Run cargo run again, and you should see the following input:

A path with nothing in it: Path(<empty>)
A path with two components: Path(/ideas/game)

Append slice of components to a path

We'll now append a slice of Components using Path::append_components.

Add the following code to src/main.rs:

use willow25::prelude::*;

fn main() {
    let empty_path = Path::new();
    println!("A path with nothing in it: {:?}", empty_path);

    let path = Path::new();
    let component1 = component!("ideas");
    let component2 = component!("game");
    let ideas_path = path.append_component(component1).unwrap();
    let ideas_game_path = ideas_path.append_component(component2).unwrap();
    println!("A path with two components: {:?}", ideas_game_path);

    let ideas_game_path2 = path.append_components(&[component1, component2]).unwrap();
    println!("Another path with two components: {:?}", ideas_game_path2);
}

Run cargo run again, and you should see the following output:

A path with nothing in it: Path(<empty>)
A path with two components: Path(/ideas/game)
Another path with two components: Path(/ideas/game)

Create a new path with a slice

We'll now create a new Path with a slice of many Component.

Add the following code to src/main.rs:

use willow25::prelude::*;

fn main() {
    let empty_path = Path::new();
    println!("A path with nothing in it: {:?}", empty_path);

    let path = Path::new();
    let component1 = component!("ideas");
    let component2 = component!("game");
    let ideas_path = path.append_component(component1).unwrap();
    let ideas_game_path = ideas_path.append_component(component2).unwrap();
    println!("A path with two components: {:?}", ideas_game_path);

    let ideas_game_path2 = path.append_components(&[component1, component2]).unwrap();
    println!("Another path with two components: {:?}", ideas_game_path2);

    let component3 = component!("design");
    let long_path = Path::from_components(&[component1, component2, component3]).unwrap();
    println!("A path with three components: {:?}", long_path);
}

Run cargo run again, and you should see the following output:

A path with nothing in it: Path(<empty>)
A path with two components: Path(/ideas/game)
Another path with two components: Path(/ideas/game)
A path with three components: Path(/ideas/game/design)

Check if a path is a prefix of another

Next we'll use Path::is_prefix_of to check if one Path is a prefix of another.

Add the following code to src/main.rs:

use willow25::prelude::*;

fn main() {
    let empty_path = Path::new();
    println!("A path with nothing in it: {:?}", empty_path);

    let path = Path::new();
    let component1 = component!("ideas");
    let component2 = component!("game");
    let ideas_path = path.append_component(component1).unwrap();
    let ideas_game_path = ideas_path.append_component(component2).unwrap();
    println!("A path with two components: {:?}", ideas_game_path);

    let ideas_game_path2 = path.append_components(&[component1, component2]).unwrap();
    println!("Another path with two components: {:?}", ideas_game_path2);

    let component3 = component!("design");
    let long_path = Path::from_components(&[component1, component2, component3]).unwrap();
    println!("A path with three components: {:?}", long_path);

    if ideas_path.is_prefix_of(&long_path) {
        println!(
            "ideas_path ({:?}) is a prefix of long_path ({:?})!",
            ideas_path, long_path
        );
    }
}

Run cargo run again, and you should see the following output:

A path with nothing in it: Path(<empty>)
A path with two components: Path(/ideas/game)
Another path with two components: Path(/ideas/game)
A path with three components: Path(/ideas/game/design)
ideas_path (Path(/ideas)) is a prefix of long_path (Path(/ideas/game/design))!

Iterate through all prefixes of a path

Next we'll iterate through all of a Path's possible prefixes using Path::all_prefixes.

Add the following code to src/main.rs:

use willow25::prelude::*;

fn main() {
    let empty_path = Path::new();
    println!("A path with nothing in it: {:?}", empty_path);

    let path = Path::new();
    let component1 = component!("ideas");
    let component2 = component!("game");
    let ideas_path = path.append_component(component1).unwrap();
    let ideas_game_path = ideas_path.append_component(component2).unwrap();
    println!("A path with two components: {:?}", ideas_game_path);

    let ideas_game_path2 = path.append_components(&[component1, component2]).unwrap();
    println!("Another path with two components: {:?}", ideas_game_path2);

    let component3 = component!("design");
    let long_path = Path::from_components(&[component1, component2, component3]).unwrap();
    println!("A path with three components: {:?}", long_path);

    if ideas_path.is_prefix_of(&long_path) {
        println!(
            "ideas_path ({:?}) is a prefix of long_path ({:?})!",
            ideas_path, long_path
        );
    }

    for prefix in long_path.all_prefixes() {
        println!("{:?} is a prefix of long_path!", prefix)
    }
}

Run cargo run again, and you should see the following output:

A path with nothing in it: Path(<empty>)
A path with two components: Path(/ideas/game)
Another path with two components: Path(/ideas/game)
A path with three components: Path(/ideas/game/design)
ideas_path (Path(/ideas)) is a prefix of long_path (Path(/ideas/game/design))!
Path(<empty>) is a prefix of long_path!
Path(/ideas) is a prefix of long_path!
Path(/ideas/game) is a prefix of long_path!
Path(/ideas/game/design) is a prefix of long_path!

Determine the longest common prefix of two paths

We'll use Path::longest_common_prefix to determine the longest common prefix of two Paths.

Add the following code to src/main.rs:

use willow25::prelude::*;

fn main() {
    let empty_path = Path::new();
    println!("A path with nothing in it: {:?}", empty_path);

    let path = Path::new();
    let component1 = component!("ideas");
    let component2 = component!("game");
    let ideas_path = path.append_component(component1).unwrap();
    let ideas_game_path = ideas_path.append_component(component2).unwrap();
    println!("A path with two components: {:?}", ideas_game_path);

    let ideas_game_path2 = path.append_components(&[component1, component2]).unwrap();
    println!("Another path with two components: {:?}", ideas_game_path2);

    let component3 = component!("design");
    let long_path = Path::from_components(&[component1, component2, component3]).unwrap();
    println!("A path with three components: {:?}", long_path);

    if ideas_path.is_prefix_of(&long_path) {
        println!(
            "ideas_path ({:?}) is a prefix of long_path ({:?})!",
            ideas_path, long_path
        );
    }

    for prefix in long_path.all_prefixes() {
        println!("{:?} is a prefix of long_path!", prefix)
    }

    let component4 = Component::new(b"art").unwrap();
    let another_long_path = Path::from_components(&[component1, component2, component4]).unwrap();
    println!(
        "The longest common prefix of long_path and another_long_path is: {:?}",
        ideas_path.longest_common_prefix(&another_long_path)
    );
}

Run cargo run again, and you should see the following output:

A path with nothing in it: Path(<empty>)
A path with two components: Path(/ideas/game)
Another path with two components: Path(/ideas/game)
A path with three components: Path(/ideas/game/design)
ideas_path (Path(/ideas)) is a prefix of long_path (Path(/ideas/game/design))!
Path(<empty>) is a prefix of long_path!
Path(/ideas) is a prefix of long_path!
Path(/ideas/game) is a prefix of long_path!
Path(/ideas/game/design) is a prefix of long_path!
The longest common prefix of long_path and another_long_path is: Path(/ideas)

Create a path with the path! macro

Finally we'll use path macro to easily create a new Path.

Add the following code to src/main.rs:

use willow25::prelude::*;

fn main() {
    let empty_path = Path::new();
    println!("A path with nothing in it: {:?}", empty_path);

    let path = Path::new();
    let component1 = component!("ideas");
    let component2 = component!("game");
    let ideas_path = path.append_component(component1).unwrap();
    let ideas_game_path = ideas_path.append_component(component2).unwrap();
    println!("A path with two components: {:?}", ideas_game_path);

    let ideas_game_path2 = path.append_components(&[component1, component2]).unwrap();
    println!("Another path with two components: {:?}", ideas_game_path2);

    let component3 = component!("design");
    let long_path = Path::from_components(&[component1, component2, component3]).unwrap();
    println!("A path with three components: {:?}", long_path);

    if ideas_path.is_prefix_of(&long_path) {
        println!(
            "ideas_path ({:?}) is a prefix of long_path ({:?})!",
            ideas_path, long_path
        );
    }

    for prefix in long_path.all_prefixes() {
        println!("{:?} is a prefix of long_path!", prefix)
    }

    let component4 = Component::new(b"art").unwrap();
    let another_long_path = Path::from_components(&[component1, component2, component4]).unwrap();
    println!(
        "The longest common prefix of long_path and another_long_path is: {:?}",
        ideas_path.longest_common_prefix(&another_long_path)
    );

    let path2 = path!("/ideas/game/music");
    println!("A path created with the path! macro: {:?}", path2);
}

Run cargo run again, and you should see the following output:

A path with nothing in it: Path(<empty>)
A path with two components: Path(/ideas/game)
Another path with two components: Path(/ideas/game)
A path with three components: Path(/ideas/game/design)
ideas_path (Path(/ideas)) is a prefix of long_path (Path(/ideas/game/design))!
Path(<empty>) is a prefix of long_path!
Path(/ideas) is a prefix of long_path!
Path(/ideas/game) is a prefix of long_path!
Path(/ideas/game/design) is a prefix of long_path!
The longest common prefix of long_path and another_long_path is: Path(/ideas)
A path created with the path! macro: Path(/ideas/game/music)

Summary

In this tutorial we used the Path API to construct and compare paths:

  • We added the willow25 crate as a dependency to a Rust project.
  • We constructed an empty Path.
  • We appended Components to an empty Path.
  • We appended a slice of Components to an empty Path.
  • We constructed a new Path with a slice of Components.
  • We checked if one Path was a prefix of another.
  • We iterated through all possible prefixes of a Path.
  • We determined the longest common prefix of two Paths.
  • We created a new Path with the path macro.