A few simple problems

You can create a "problems" Rust project in your repository to try your solutions to those simple problems.

Lifetimes

The trim method on str (and thus on String thanks to Deref) removes the blanks at the beginning and at the end of a string. Its signature is:

fn trim(&self) -> &str;

which is, thanks to lifetimes elision, a shortcut for

fn trim<'a>(&'a self) -> &'a str;

Who is the owner?

The following code fails to compile:

fn ret_string() -> String {
    String::from("  A String object  ")
}

fn main() {
    let s = ret_string().trim();
    assert_eq!(s, "A String object");
}

Why? Ask yourself: what is the lifetime of s? Who is the owner of the underlying string with spaces (every object has an owner)?

❎ Fix the code so that it compiles (and the s variable represents the trimmed string). Note that you can reuse the same variable name.

Select between alternatives

❎ Add the most appropriate lifetimes to the following function:

fn choose_str(s1: &str, s2: &str, select_s1: bool) -> &str {
    if select_s1 { s1 } else { s2 }
}

At call time, s1 and s2 may have different lifetimes and we don't want any constraint between the lifetimes of those two strings.

Write a OOR (owned or ref) type

For this problem, do not look at the standard Cow type.

We want to create a OOR type which can store either a String or a &str to avoid copying a string which already exists in the environment.

❎ Write a OOR enum with two alternatives: Owned which stored a String and Borrowed which stores a &str.

It will require using a generic parameter. What does it represent?

❎ Implement the Deref trait for the OOR structure so that it dereferences into an a &str. What is the lifetime of the resulting &str (note that you have no choice there)? Why is that always appropriate?

❎ Check that you can now call &str methods directly on an arbitrary OOR object by writing some tests.

❎ Write a DerefMut trait for the OOR structure. If you have not stored a String, you will have to mutate and store a String before you can hand out a &mut str because you can't transform your inner &str into &mut str.

❎ Check that you can run the following test:

// Check Deref for both variants of OOR
let s1 = OOR::Owned(String::from("  Hello, world.  "));
assert_eq!(s1.trim(), "Hello, world.");
let mut s2 = OOR::Borrowed("  Hello, world!  ");
assert_eq!(s2.trim(), "Hello, world!");

// Check choose
let s = choose_str(&s1, &s2, true);
assert_eq!(s.trim(), "Hello, world.");
let s = choose_str(&s1, &s2, false);
assert_eq!(s.trim(), "Hello, world!");

// Check DerefMut, a borrowed string should become owned
assert!(matches!(s1, OOR::Owned(_)));
assert!(matches!(s2, OOR::Borrowed(_)));
unsafe {
    for c in s2.as_bytes_mut() {
        if *c == b'!' {
            *c = b'?';
        }
    }
}
assert!(matches!(s2, OOR::Owned(_)));
assert_eq!(s2.trim(), "Hello, world?");