I started this lesson with a word I had heard spoken: “deck.” In a Rust discussion, that likely meant deque, pronounced “deck,” short for double-ended queue.
A deque is a collection where items can be added or removed at either end. To understand it, I created a small Rust project on Debian, edited it in Zed, and ran several experiments using card names. By the final run, I had removed a card from each end and left only Ace.
Lab Journal
Creating the project
In my terminal, I ran:
cargo new ~/rust-deque-practice
Cargo created a Rust application project. The main files and folders were:
| Item | Purpose |
|---|---|
Cargo.toml | Project settings and dependencies |
src/main.rs | The Rust source code I edited |
Cargo.lock | Records resolved dependency versions |
target/ | Build output created by Cargo |
I opened the project in Zed:
zed ~/rust-deque-practice
Inside src/main.rs, I replaced the starter “Hello, world!” program with:
use std::collections::VecDeque;
fn main() {
let mut cards = VecDeque::new();
cards.push_back("Ace");
cards.push_back("King");
cards.push_front("Queen");
println!("{cards:?}");
}
After saving with Ctrl+S, I ran these commands in the terminal:
cd ~/rust-deque-practice
cargo run
Cargo compiled and ran the program. The output was:
["Queen", "Ace", "King"]
Ace and King went to the back. Queen went to the front, which explains why Queen appeared first even though it was added last.
The editor and terminal mix-up
At one point, I pasted cd ~/rust-deque-practice and cargo run into the bottom of main.rs. Those are shell commands, so they did not belong in the Rust program.
The fix was to delete those two lines, save the file, and switch back to the terminal showing my (base) lvydvy@Debian:...$ prompt.
My working routine became: edit in Zed, save with Ctrl+S, then run cargo run in the terminal.
Trying both ends
I added removal operations immediately before println! and checked the output each time.
| Experiment | Removal code | Observed output |
|---|---|---|
| Keep all three cards | No removal | ["Queen", "Ace", "King"] |
| Remove from the front | cards.pop_front(); | ["Ace", "King"] |
| Remove from the back | cards.pop_back(); | ["Queen", "Ace"] |
| Remove from both ends | Front removal, then back removal | ["Ace"] |
Each run started the program again and created a fresh deque. The previous run’s removals did not carry over.
The Final Rust Program
use std::collections::VecDeque;
fn main() {
let mut cards = VecDeque::new();
cards.push_back("Ace");
cards.push_back("King");
cards.push_front("Queen");
cards.pop_front();
cards.pop_back();
println!("{cards:?}");
}
The final output was:
["Ace"]
Reading the Rust Code Line by Line
use std::collections::VecDeque;
This brings the name VecDeque into scope from Rust’s standard library. std means the standard library, and collections contains collection types. No extra package installation was needed.
fn main() {
fn declares a function. main is the entry point for this application. The parentheses contain no parameters, and the braces enclose the function’s body.
let mut cards = VecDeque::new();
let introduces a variable named cards. mut allows us to change the deque through that variable. VecDeque::new() creates an empty deque, and = assigns it to cards.
Rust infers the element type from the card names added later. These quoted literals are string slices, so this collection has the type VecDeque<&str>. We did not need to write that type explicitly.
cards.push_back("Ace");
The dot calls a method on cards. This adds Ace to the back. The deque now contains ["Ace"].
cards.push_back("King");
This adds King after Ace, producing ["Ace", "King"].
cards.push_front("Queen");
This adds Queen before Ace, producing ["Queen", "Ace", "King"].
cards.pop_front();
This removes Queen from the front. Ace and King remain.
cards.pop_back();
This removes King from the back. Only Ace remains.
The removal methods also return the removed item wrapped in Some(...), or None if the deque is empty. In this lesson, we discard those return values and print the remaining collection. See the Rust VecDeque documentation.
println!("{cards:?}");
This displays the remaining cards and ends the output with a newline. The ! identifies println! as a macro. Inside the format string, cards names the variable and :? requests debug formatting, which makes the collection readable as ["Ace"].
The final } closes the main function. Blank lines separate the stages for readability. Semicolons terminate the statements used here.
How This Compares with Python
Python provides the same kind of collection as collections.deque. This equivalent example is included for comparison; the terminal runs in this lesson were Rust runs.
from collections import deque
cards = deque()
cards.append("Ace")
cards.append("King")
cards.appendleft("Queen")
cards.popleft()
cards.pop()
print(list(cards))
Expected output:
['Ace']
list(cards) makes a list for display, so the output resembles the Rust example. Printing cards directly would show deque(['Ace']).
Matching the operations
| Action | Rust VecDeque | Python deque |
|---|---|---|
| Create an empty deque | VecDeque::new() | deque() |
| Add at the front | push_front("Queen") | appendleft("Queen") |
| Add at the back | push_back("Ace") | append("Ace") |
| Remove at the front | pop_front() | popleft() |
| Remove at the back | pop_back() | pop() |
Python’s removal methods return the item directly. Removing from an empty deque raises IndexError, while Rust’s methods return None. These behaviors are documented in the Python deque reference and the Rust VecDeque reference.
Differences I can recognize already
| Topic | Rust in this lesson | Python equivalent |
|---|---|---|
| Changing the collection | Requires mut on this binding | No mut keyword needed |
| Element types | One element type, checked during compilation | Can contain objects of different types |
| Program structure | Code is inside fn main() | This script can run at the top level |
| Blocks | Braces mark the function body | Indentation marks blocks when needed |
| Running | cargo run builds as needed, then runs | A saved script can run with python3 filename.py |
Python’s familiar syntax makes the idea easier to recognize. Rust asks me to be more explicit about mutation and checks types before the program runs. The operations on the cards remain the same.
A Python list can also add and remove items, but collections.deque is the closer match for this exercise. It supports efficient operations at both ends; removing the first item of a list with pop(0) shifts the remaining elements. See the Python collections documentation.
Lessons Learned
- “Deque” sounds like “deck” and means double-ended queue.
pushadds an item;popremoves one.- Front and back are positions in the collection, not alphabetical order.
- Saving in Zed and running in the terminal are separate steps.
- Each new program run rebuilds the example’s collection from scratch.
- Changing one operation at a time makes the output easier to explain.
The final ["Ace"] output showed that I had successfully added three items, removed one from each end, and understood why the middle item remained.