Blame view

ownership/src/main.rs 6.87 KB
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183
//
// 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[..]
}