diff --git a/src/string.rs b/src/string.rs index 45be15e..75749f6 100644 --- a/src/string.rs +++ b/src/string.rs @@ -1,3 +1,4 @@ +use std::rc::Rc; // when a String is passed to this function a move occurs: // BUT only the stack allocation (ptr, len, cap) is copied over @@ -10,6 +11,8 @@ fn takes_string(_s: String) { } +// You can do this, but you probably want &str +// UNLESS you want `&mut String` #[allow(dead_code)] fn takes_string_ref(_s: &String) { @@ -27,7 +30,7 @@ fn takes_string_ref(_s: &String) { // Why is this a problem? // rust doesn't know how much space to allocate for the variable // -// str inactuality is the raw (UTF-8) bytes, not a container +// str in actuality is the raw (UTF-8) bytes, not a container // fn takes_str(s: str) { } // this is fine because now the variable is a fat pointer: pointer + length @@ -42,42 +45,75 @@ fn _get_static_str() -> &'static str { "hello" } -#[test] -fn test_string() { - let s = String::new(); // no heap allocation until we add characters (capacity > 0) +// The #[cfg(test)] annotation on the tests module tells Rust to compile +// and run the test code only when you run cargo test, not when you run cargo build +#[cfg(test)] +mod tests { + use super::*; // need access to functions above - // let s_r: str; // ERROR: size not known at compile time - let _s_rr: &str; // allowed - let _s_rrr: &String; // allowed + #[test] + fn test_string() { + let s = String::new(); // no heap allocation until we add characters (capacity > 0) - takes_string_ref(&s); - takes_string_ref(&s); // works because the string was not moved in first call + // let s_r: str; // ERROR: size not known at compile time + let _s_rr: &str; // allowed + let _s_rrr: &String; // allowed - let ss = String::from("1"); // one heap allocation! - takes_string(ss); // move occurs here - // takes_string(ss); // ERROR: use of moved value here - // takes_string_ref(&ss); // ERROR: borrow of moved value here + takes_string_ref(&s); + takes_string_ref(&s); // works because the string was not moved in first call - let sss = String::new(); - // deref coersion: - // because String implements the trait Deref - takes_str_ref(&sss); - // *sss dereferences the String into its underlying str - takes_str_ref(&*sss); // same as this! - takes_str_ref(sss.as_ref()); // and this! + let ss = String::from("1"); // one heap allocation! + takes_string(ss); // move occurs here + // takes_string(ss); // ERROR: use of moved value here + // takes_string_ref(&ss); // ERROR: borrow of moved value here - let s4 = String::from("string4"); - //let s4_slice = s4[0..]; // again we hit no size problem at compile time - let _s4_slice = &s4[0..]; // but this works, because the &str has the length! + let sss = String::new(); + // deref coersion: + // because String implements the trait Deref + takes_str_ref(&sss); + // *sss dereferences the String into its underlying str + takes_str_ref(&*sss); // same as this! + takes_str_ref(sss.as_ref()); // and this! - // literals - // &'static str is held onto by &str: - // that's the programs read-only binary - let s_lit = "easy as pie, right?"; - let _s_lit2 : &str = "easy as pie, right?"; // same, data not copied, we just point to it - let _s5 = String::from("literal is copied to heap"); // data copied to heap - let _s6 = String::from(s_lit); //underlying data copied to heap + let s4 = String::from("string4"); + //let s4_slice = s4[0..]; // again we hit no size problem at compile time + let _s4_slice = &s4[0..]; // but this works, because the &str has the length! - // todo: Cow + // literals + // &'static str is held onto by &str: + // that's the programs read-only binary + let s_lit = "easy as pie, right?"; + let _s_lit2 : &str = "easy as pie, right?"; // same, data not copied, we just point to it + let _s5 = String::from("literal is copied to heap"); // data copied to heap + let _s6 = String::from(s_lit); //underlying data copied to heap + + // Box is owned, heap allocated, fixed size, immutable + // where as `&str` is borrowed view, linked to source, immutable + // both have (ptr, len) -> underlying mem + let _s7 : Box = "hello".into(); + + // Rc (single-threaded reference counting) + // HEAP memory layout: + // * counter (strong) + // * counter (weak) + // * len + // * data + // STACK memory: + // * ptr + // * len + // + // Why are there two lengths? + let rc_str : Rc = Rc::from("rc_str"); + // let rc_str2 : Rc = rc_str; // this moves + let rc_str2 : Rc = rc_str.clone(); // copy ptr, increment counter + println!("printing {}", rc_str); + println!("printing {}", rc_str); + println!("printing {}", rc_str2); + + // todo: Arc + // todo: Cow + // todo: byte strings: &[u8] and Vec + // todo: Os & platform strings: OsStr & OsString, Path & PathBuf + // todo: ffi strings: CString & CStr + } } -