main.rs 6.87 KB
//
// Examples related to ownership, also introducing String.
//
// Georg Hopp <georg@steffers.org>
//
// Copyright © 2019 Georg Hopp
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.
//

fn main() {
    concat();
    move_variable();
    clone_variable();

    let s = String::from("hello");  // s comes into scope

    take_ownership(s);              // s's value moves into the function
                                    // … and so is no longer valid here.
    let x = 5;                      // x comes into scope

    makes_copy(x);                  // the scalar value has the Copy trait and
                                    // lives on the stack and thus is still
                                    // valid here.

    let _s1 = gives_ownership();     // move the return value into _s1
    let  s2 = String::from("hello"); // s2 comes into scope
    let _s3 = takes_and_gives(s2);   // s2 is moved into function, which in
                                     // turn moves the return value into _s3.

    let s1 = String::from("hello");  // s1 comes into scope.
    let len = calculate_length(&s1); // A reference to s1 is given to a
                                     // calculate_length which effectively is
                                     // not s1 itself but another pointer to
                                     // s1… which is the reason that we still
                                     // can use it below.
    let _another_s = &s1;            // again only a reference which does not
                                     // take ownership, thus s1 can still be
                                     // used below.
    println!("The length of '{}' is {}.", s1, len);

    // Passing values as reference to a function is called borrowing. A
    // borrowed value can't be changed.
    change(&s1);

    // but we can force this… which is probably not the best of ideas most
    // of the time…
    let mut s_mut = String::from("hello");
    change_mutable(&mut s_mut);

    // but you can have only one mutable reference of a value in a single
    // scope. The following would fail with:
    // cannot borrow `s_mut` as mutable more than once at a time second
    // mutable borrow occurs here
    // let r1 = &mut s_mut;
    // let r2 = &mut s_mut;
    // println!("{}, {}", r1, r2);

    // We also can't have an immutable reference while we have a mutable one.
    // Look on Page 98 for an explanation.

    // The scope of references is not the whole block they are introduced in
    // but goes only until their last usage. Thus if you first have an
    // immutable reference but never use it after a mutable reference is
    // declared, that would be ok… At all this is kind of confusing and very
    // Mozzilaish. :D

    // Now we demonstrate string slices…
    let s4 = String::from("hello world");
    let s_slice = first_word(&s4);

    // working with an mutable reference like with s4.clear() will not
    // compile at this point because we already have and use later on an
    // immutable reference.

    println!("The slice was: {}", s_slice);

    // not that string literals are slices. They are immutable references of
    // the programs TEXT segment. Thats the reason why they are immutable.

    // Thus try generic_first_word…
    println!("First word on literal: {}", generic_first_word("hello world"));
    println!("First word on String: {}", generic_first_word(&s4[..]));
} // x and s go out of scope but nothing happens for s because this function
  // has no longer the ownership of s.
  // s3 goes out of scope and is dropped. s2 was moved and s1 is dropped.

fn concat() {
    let mut s = String::from("hello");
    s.push_str(", world!");
    println!("{}", s);
}

fn move_variable() {
    let s1 = String::from("hello");
    let s2 = s1; // does not copy data but only the String structure.
    // when using s1 below we get an error that a moved value was borrowed.
    println!("{}, world!", s2);
}

fn clone_variable() {
    let s1 = String::from("hello");
    let s2 = s1.clone();
    // this time both are valid.
    println!("s1 = {}, s2 = {}", s1, s2)
}

fn take_ownership(some_string: String) { // some_string comes into scope
    println!("{}", some_string);
} // some_string goes out of scope and «drop» is called, thus memory freed.

fn makes_copy(some_integer: i32) { // some integer comes into scope
    println!("{}", some_integer);
} // Here, some_integer goes out of scope but because it was a copy and on the
  // stack nothing special happens… beside that stack space is freed.

fn gives_ownership() -> String { // this will move the return value into the
                                 // calling function.
    let some_string = String::from("hello"); // some_string comes into scope
    some_string
}

fn takes_and_gives(a_string: String) -> String { // a_string comes into scope
    a_string                                     // and is returned and moved
                                                 // to the calling function.
} // a_string goes out of scope but nothing happens as it is moved.

fn calculate_length(s: &String) -> usize { // s comes into scope. It is a
                                           // reference. References do not
                                           // take ownership of the underlying
                                           // value which is the String in
                                           // main.
    s.len()
} // Here s goes out of scope but because it has no ownership of the String
  // nothing happens.

fn change(_some_string: &String) {
    // the following would give this error:
    // `_some_string` is a `&` reference, so the data it refers to cannot be
    // borrowed as mutable
    // _some_string.push_str(", world!");
}

fn change_mutable(some_string: &mut String) {
    some_string.push_str(", world");
}

fn first_word(s: &String) -> &str {
    let bytes = s.as_bytes();

    for (i, &item) in bytes.iter().enumerate() {
        if item == b' ' {
            return &s[..i];
        }
    }

    &s[..]
}

// To make first_word work on either string literals (which are in fact string
// slices, s.o.) one would write first_word like this…
fn generic_first_word(s: &str) -> &str {
    let bytes = s.as_bytes();

    for (i, &item) in bytes.iter().enumerate() {
        if item == b' ' {
            return &s[..i];
        }
    }

    &s[..]
}