Strings
There are two string types in Rust: String
and &str
. The former is
allocated on the heap and the latter is a slice of a String
or a &str
.
The mapping of those to Java is shown in the following table:
Rust | Java | Note |
---|---|---|
&str | String | see Note 1. |
Box<str> | String | see Note 2. |
String | String | |
String (mutable) | StringBuilder | see Note 2. |
There are differences in working with strings in Rust and Java, but the
equivalents above should be a good starting point. One of the differences is
that Rust strings are UTF-8 encoded, but JVM strings are UTF-16 encoded.
Further JVM strings are immutable, but Rust strings can be mutable when declared
as such, for example let s = &mut String::from("hello");
.
There are also differences in using strings due to the concept of ownership. To read more about ownership with the String Type, see the Rust book.
Notes:
-
In Rust,
&str
(pronounced: string slice) is an immutable string reference type. -
The
Box<str>
type in Rust is equivalent to theString
type in JVM. The difference between theBox<str>
andString
types in Rust is that the former stores pointer and size while the latter stores pointer, size, and capacity, allowingString
to grow in size. This is similar to theStringBuilder
type in JVM once the RustString
is declared mutable.
Java:
String str = "Hello, World!";
StringBuilder sb1 = new StringBuilder("Hello, World!");
StringBuilder sb2 = new StringBuilder();
sb2.append("Hello");
Rust:
let str1: &str = "Hello, World!";
let str2 = Box::new("Hello World!");
let mut sb1 = String::from("Hello World!");
let mut sb2 = String::new();
sb2.push_str("Hello");
String Literals
String literals in Java are immutable String
types and allocated on the heap.
In Rust, they are &'static str
, which is immutable and has a global lifetime
and does not get allocated on the heap; they're embedded in the compiled binary.
Java
String str = "Hello, World!";
Rust
let str: &'static str = "Hello, World!";
Unlike Java, Rust can represent verbatim string literals as raw string literals.
Rust
let str = r#"Hello, \World/!"#;
String Interpolation
Java lacks native support for String interpolation1 in comparison to languages like C#. The most common way of implementing string interpolation in Java is by using the format()
method from the String
class. Here's an example:
String name = "John";
int age = 42;
String result = String.format("Person { Name: %s, Age: %d }", name, age);
// Alternative using the '+' operator
String result2 = "Person { Name: " + name + ", Age: " + age + " }";
Like Java, Rust does not have a built-in string interpolation feature. Instead, the
format!
macro is used to format a string. The following example shows how to
use string interpolation in Rust:
let name = "John";
let age = 42;
let result = format!("Person {{ name: {name}, age: {age} }}");
Custom types can also be interpolated in Java due to the fact that
the toString()
method is available for each type as it's inherited from Object
.
public class Person {
private String name;
private int age;
// getters and setters omitted
@Override
public String toString() {
return String.format("Person { Name: %s, Age: %d }", name, age);
}
}
// Calling from main method
Person person = new Person();
person.setName("John");
person.setAge(42);
System.out.println(person);
In Rust, there is no default formatting implemented/inherited for each type.
Instead, the std::fmt::Display
trait must be implemented for each type that
needs to be converted to a string.
use std::fmt::*;
struct Person {
name: String,
age: i32,
}
impl Display for Person {
fn fmt(&self, f: &mut Formatter<'_>) -> Result {
write!(f, "Person {{ name: {}, age: {} }}", self.name, self.age)
}
}
let person = Person {
name: "John".to_owned(),
age: 42,
};
println!("{person}");
Another option is to use the std::fmt::Debug
trait. The Debug
trait is
implemented for all standard types and can be used to print the internal
representation of a type. The following example shows how to use the derive
attribute to print the internal representation of a custom struct using the
Debug
macro. This declaration is used to automatically implement the Debug
trait for the Person
struct:
#[derive(Debug)]
struct Person {
name: String,
age: i32,
}
let person = Person {
name: "John".to_owned(),
age: 42,
};
println!("{person:?}");
Note: Using the :? format specifier will use the
Debug
trait to print the struct, where leaving it out will use theDisplay
trait.
See also:
Java now has String Templates as a preview feature in Java 21 and Java 22.