Slices and Strings with Rust: Goodbye C# StringBuilder?

When you’ve spent years writing C#, you get really comfortable with string being immutable, Span<T> being your performance trick, and StringBuilder being your go-to hammer when a for loop starts building text.

And then you start learning Rust.

Suddenly, String isn’t immutable. &str looks suspiciously like a Span<char> in disguise. And you realize… wait, do I even need a StringBuilder anymore?

Today on Day 12, I dove into slices and strings in Rust, and let me tell you, it’s a whole new world, but a surprisingly elegant one.

Strings in C#: Immutable, Safe, and Everywhere

You probably know this dance by heart:

string name = "Alice";
string upper = name.ToUpper();
Console.WriteLine(name); // still "Alice"

Strings in .NET are immutable reference types backed by UTF-16. If you want to modify one, you either:

  • Reassign the variable, or

  • Reach for a StringBuilder if performance matters

You also get tools like Span<char> and Memory<T> for performance-critical code when slicing or manipulating buffers.

Strings in Rust: A Bit More to Unpack

Rust splits strings into two distinct types:

  • String – a growable, heap-allocated UTF-8 string

  • &str – a string slice, referencing a part of a String (or a string literal)

fn main() {
    let name = String::from("Alice");
    let greeting = format!("Hello, {}!", name);
    println!("{}", greeting);
}

You can modify String directly:

let mut msg = String::from("Hello");
msg.push_str(", world");
println!("{}", msg); // Hello, world

Yep, no StringBuilder needed. Just mutate the String.

&str: The Slice-y Sidekick

The &str type is like a read-only view into a String. Think of it like a Span<char> in C#, but with guaranteed safety and lifetimes checked at compile time.

fn greet(name: &str) {
    println!("Hi, {}!", name);
}

fn main() {
    let user = String::from("Alice");
    greet(&user); // passing a &str
}

You can even slice strings with range syntax:

let text = String::from("Rustacean");
let slice = &text[0..4]; // "Rust"
println!("{}", slice);

But here’s the catch: Rust strings are UTF-8 encoded. So slicing is byte-based, not char-based. Slicing in the middle of a multibyte character? Compiler panic.

Comparing with C#: Span Vibes

Rust’s &str gives you the safety and flexibility of a C# ReadOnlySpan<char>, but with much tighter compiler enforcement.

And if you want something like a Span<u8> in Rust, just use a &[u8] slice:

let bytes = b"hello"; // byte string literal

There’s no need to pin memory, worry about unsafe access, or juggle multiple string types. Rust’s system is consistent, even if it’s more verbose at times.

UTF-8 vs UTF-16: Why It Matters

C# strings are UTF-16. That means most common characters (including emojis) take 2 bytes, but some use surrogate pairs. Slicing blindly is usually okay, but not always safe.

Rust strings are UTF-8, which is smaller for ASCII and more web-native. But it also means:

let smile = "😊";
println!("{}", &smile[0..1]); // ERROR!

Why? Because you’re trying to cut a byte in the middle of a 4-byte emoji. Rust protects you from doing that by accident. Thanks, borrow checker (again).

Do You Ever Need StringBuilder in Rust?

Not really.

With String, you can:

  • Append with push_str() or push()

  • Format with format!() (like string.Format() or C# interpolated strings)

  • Replace substrings, trim, split… everything you expect

If you need high-performance streaming or chunked writes, you’d probably reach for std::fmt::Write or buffered I/O, but for most devs, String just does the job.

Final Thoughts: It’s Simpler Than It Looks

At first, having String and &str felt like one type too many. But now? I get it.

Rust separates string ownership from string views. And once you embrace slices, you realize they’re everywhere: strings, arrays, buffers. It’s one concept to learn that applies to multiple types.

Tomorrow, we examine shadowing, specifically re-declaring variables with the same name intentionally. It may sound unusual, but it’s actually quite elegant. Stay tuned.

Work with me

Need this kind of thinking on your team?

Fractional architecture, a decision brief, or a workshop built around the problems in these articles.

Get in touch