Originally published on php2rust.dev — part of the PHP to Rust course. View this lesson on the original site.

Rust has two main string types: String (owned, growable) and &str (borrowed slice). PHP has a single string type that is copy-on-write.

From the previous lesson: On Slices we introduced borrowing and slice views, how &[T] lets you point at part of an array or Vec without owning or copying the whole thing. &str (“string slice”) is the same pattern for text: a borrowed view into UTF-8 bytes, usually inside a String or a string literal baked into the program. Keep that mental model: String owns the buffer; &str borrows a region of it (or borrows immortal literal text).

Rust

fn main() {
    // &str: string slice (literal or borrowed)
    let literal: &str = "Hello, world!";
    println!("literal: {}", literal);

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

    // Convert
    let from_str: String = "hi".to_string();
    println!("from_str: {}", from_str);
    let slice: &str = &s[0..5];
    println!("slice: {}", slice);
}
  • &str: fixed view; often used in function parameters.
  • String: heap-allocated, mutable.
  • Both are UTF-8.

Use case (PHP mindset): pass “some text”, not an owned String

In PHP you might write functions that accept a string or a substring (e.g. something like substr($s, 0, 5)), but Rust splits this into types:

  • &str is a borrowed view into text. Since it borrows, you can’t “delete”/drop the original String while the &str is still being used (Rust will refuse to compile).
  • &mut str is a mutable borrow into a substring. Because it borrows the same underlying text, mutating that substring changes the original String.
fn starts_with_hello(text: &str) -> bool {
    text.starts_with("Hello")
}

fn main() {
    let s = String::from("Hello, world!");

    // Borrow the first 5 bytes as a `&str`
    let first_five: &str = &s[0..5];
    println!("first_five starts with Hello? {}", starts_with_hello(first_five));

    // Mutable borrow: change a substring, and the original `String` changes too
    let mut s2 = String::from("hello");
    {
        let sub: &mut str = &mut s2[0..5];
        sub.make_ascii_uppercase(); // "HELLO"
    }
    println!("s2 is now: {}", s2);
}

PHP

<?php
$literal = "Hello, world!";
$s = "Hello";
$s .= ", world!";
echo "$s\n";

// Single type, copy-on-write
$from_str = "hi";
$sub = substr($s, 0, 5);  // New string

PHP strings are mutable in place (with copy-on-write). They can be any byte sequence but are typically UTF-8 when handling text.

Comparison

  • Rust: String (owned) and &str (borrowed). Always UTF-8. No direct byte indexing.
  • PHP: Single string type. Mutable. Use mb_* for proper character handling.

References

What's next

Next: Structs.

From PHP to Rust — Rust Syntax and Functions