#string #buffer #concat

str-cat

Efficiently concatenate strings without extra side-effects

5 releases

0.2.0 Mar 2, 2023
0.1.3 Feb 28, 2023
0.1.2 Feb 24, 2023
0.1.1 Feb 24, 2023
0.1.0 Feb 24, 2023

#254 in Value formatting

BSD-3-Clause

12KB
109 lines

str-cat

This crate provides macros to efficiently concatenate strings without extra side-effect.

GitHub Workflow Status docs.rs

Documentation


lib.rs:

This crate provides macros to efficiently concatenate strings without extra side-effect.

Usage

Basic usage

use str_cat::str_cat;

let s = str_cat!("Hello", " ", "World", "!");
assert_eq!(s, "Hello World!");

which is roughly equivalent to

let mut s = String::with_capacity("Hello".len() + " ".len() + "World".len() + "!".len());
s.push_str("Hello");
s.push_str(" ");
s.push_str("World");
s.push_str("!");

No extra side-effect

The macro runs without extra side-effect, which means all involved expressions are evaluated exactly once.

let mut get_world_calls = 0;
let mut get_world = || {
    get_world_calls += 1;
    "World"
};
let s = str_cat!("Hello", " ", get_world(), "!");
assert_eq!(s, "Hello World!");
assert_eq!(get_world_calls, 1);

which is roughly equivalent to

let world = get_world(); // evaluate the expression and store it for later use
let mut s = String::with_capacity("Hello".len() + " ".len() + world.len() + "!".len());
s.push_str("Hello");
s.push_str(" ");
s.push_str(&world);
s.push_str("!");

Append to an existing string

let mut s = "Hello".to_owned();
str_cat!(&mut s; " ", "World!");
assert_eq!(s, "Hello World!");

Reuse existing allocation

let mut s = "Hello World!".to_owned();
let ptr = s.as_ptr();
let cap = s.capacity();

s.clear();
str_cat!(&mut s; "Hello");
assert_eq!(s, "Hello");
// Did not grow
assert_eq!(s.as_ptr(), ptr);
assert_eq!(s.capacity(), cap);

Custom minimum capacity

let s = str_cat!(String::with_capacity(16); "foo", "bar");
assert_eq!(s, "foobar");
assert!(s.capacity() >= 16);

Argument types

Works with any expressions that can dereference to str when evaluated. Although it should be more simple and efficient to use format! instead when you can't avoid explicit .to_string() calls.

// Just an example. It's better to use `format!` in this case.
let s = str_cat!(
    "Hello".to_owned(),
    Box::new(" "),
    ['W', 'o', 'r', 'l', 'd'].iter().collect::<String>(),
    '!'.to_string(),
    123456.to_string(),
);
assert_eq!(s, "Hello World!123456");

Implicit borrowing

Just like format! and println!, str_cat automatically borrows the arguments for you, so you don't have to type that & yourself.

let world = "World!".to_owned();
let s = str_cat!("Hello ", world); // no need for `&`
assert_eq!(s, "Hello World!");
assert_eq!(world, "World!"); // not moved, still valid

Variants

There are also variants for PathBuf, OsString and Vec.

use str_cat::os_str_cat;

// Works for anything that implements AsRef<OsStr>.
let s = os_str_cat!(
    OsStr::new("Hello"),
    OsStr::new(" ").to_owned(),
    Path::new("World"),
    "!",
);
assert_eq!(s, OsStr::new("Hello World!"));

No runtime deps