Question
I want to combine the elements of a Vec<String> into a single String in Rust, similar to a join operation in other languages.
For example:
let string_list = vec!["Foo".to_string(), "Bar".to_string()];
let joined = /* how do I join these with "-"? */;
assert_eq!("Foo-Bar", joined);
What is the Rust way to join a vector of strings with a separator such as "-"?
A related question is how this compares to idiomatic string output when printing iterator items separated by spaces.
Short Answer
By the end of this page, you will understand how to join multiple strings into one String in Rust, especially from a Vec<String> or string slices. You will learn the join() method, what types it works on, how separators are used, and common patterns developers use in real Rust code.
Concept
In Rust, joining strings means taking multiple string values and combining them into one larger String, with an optional separator placed between each item.
The most common tool for this is the join() method on slices of strings. In practice, this is often used with:
Vec<String>Vec<&str>- slices like
&[String] - collections that can be viewed as string slices
For example, if you have these values:
"Foo""Bar""Baz"
and join them with "-", the result is:
Foo-Bar-Baz
This matters because combining text is very common in real programs:
- building file paths or labels
- generating CSV-like output
- constructing log messages
- formatting command-line output
- turning a list of values into a readable string
In Rust, string handling is explicit and type-safe. That means it is important to know whether you are working with:
String— owned, growable string data
Mental Model
Think of join() like connecting train cars with a coupling placed between each car.
- Each string is a train car.
- The separator is the coupling, such as
"-"or", ". - The final result is one complete train.
If your list is:
["Foo", "Bar", "Baz"]
and your separator is "-", then join() builds:
Foo-Bar-Baz
Important detail: the separator goes between items, not before the first item and not after the last item.
Syntax and Examples
The basic Rust syntax is:
let joined = vec.join(separator);
Example with Vec<String>
fn main() {
let string_list = vec!["Foo".to_string(), "Bar".to_string()];
let joined = string_list.join("-");
assert_eq!("Foo-Bar", joined);
}
Why this works
string_listis aVec<String>join("-")combines all elements"-"is inserted between each element- the result is a new
String
Example with Vec<&str>
Step by Step Execution
Consider this example:
fn main() {
let items = vec!["Foo".to_string(), "Bar".to_string(), "Baz".to_string()];
let result = items.join("-");
println!("{}", result);
}
Here is what happens step by step:
- Rust creates a vector named
items. - The vector contains three
Stringvalues:"Foo""Bar""Baz"
items.join("-")is called.- Rust starts building a new
Stringfor the result. - It adds the first item:
"Foo" - It sees another item exists, so it adds the separator:
"-"
Real World Use Cases
Joining strings appears in many common Rust tasks.
Building command output
let args = vec!["cargo", "build", "--release"];
let command = args.join(" ");
// "cargo build --release"
Creating CSV-style text
let fields = vec!["Alice", "24", "Engineer"];
let line = fields.join(",");
// "Alice,24,Engineer"
Formatting tags or labels
let tags = vec!["rust", "strings", "beginner"];
let display = tags.join(" | ");
// "rust | strings | beginner"
Constructing paths or keys
Real Codebase Usage
In real Rust codebases, developers often use join() after transforming data into text.
Pattern: map values to strings, then join
fn main() {
let numbers = vec![10, 20, 30];
let output = numbers
.iter()
.map(|n| n.to_string())
.collect::<Vec<_>>()
.join(", ");
assert_eq!("10, 20, 30", output);
}
This pattern is common when original values are not strings.
Pattern: validation before joining
fn join_names(names: Vec<String>) -> String {
if names.is_empty() {
return "No names".to_string();
}
names.join(", ")
}
Common Mistakes
1. Trying to call join as a free function
Broken idea:
let joined = something::join(string_list, "-");
Why it is wrong:
- In Rust,
joinis usually called as a method on the collection or slice.
Correct version:
let joined = string_list.join("-");
2. Forgetting that join() returns a new String
let parts = vec!["a".to_string(), "b".to_string()];
let result = parts.join("-");
result is a new owned . It does not modify each element in place.
Comparisons
| Concept | What it does | Separator support | Typical use |
|---|---|---|---|
join() | Combines string elements into one String | Yes | Human-readable text, CSV-style lines, paths |
concat() | Combines string elements into one String | No | Simple direct concatenation |
format!() | Builds a formatted string from values | Manual | Custom sentence or message formatting |
Manual loop with push_str() | Appends text piece by piece | Yes, but manual | Fine-grained control |
vs
Cheat Sheet
Quick syntax
let result = items.join(", ");
Works well with
Vec<String>Vec<&str>- slices of string-like values
Examples
let a = vec!["Foo".to_string(), "Bar".to_string()];
let b = a.join("-");
// "Foo-Bar"
let a = vec!["x", "y", "z"];
let b = a.join("");
// "xyz"
Use concat() when no separator is needed
FAQ
How do I join a Vec<String> in Rust?
Use the join() method:
let items = vec!["Foo".to_string(), "Bar".to_string()];
let result = items.join("-");
Can I join a Vec<&str> too?
Yes.
let items = vec!["Foo", "Bar"];
let result = items.join("-");
What does join() return in Rust?
It returns a new owned String.
Does join() add the separator at the end?
No. The separator is only inserted between items.
What happens if the vector is empty?
Mini Project
Description
Build a small Rust utility that turns a list of tags into a display string. This demonstrates how join() is used in real applications to format user-facing text from a collection of values.
Goal
Create a function that accepts a list of tags and returns a single comma-separated string.
Requirements
- Create a function that takes a
Vec<String>. - Return all tags joined with
", ". - If the vector is empty, return
"No tags". - In
main, test the function with both a non-empty list and an empty list.
Keep learning
Related questions
Accessing Cargo Package Metadata in Rust
Learn how to read Cargo package metadata like version, name, and authors in Rust using compile-time environment macros.
Default Function Arguments in Rust: What to Use Instead
Learn how Rust handles default function arguments, why they are not supported, and practical patterns to achieve similar behavior.
Fixing Rust "linker 'cc' not found" on Debian in WSL
Learn why Rust shows "linker 'cc' not found" on Debian in WSL and how to fix it by installing the required C build tools.