refer to some value without taking ownership of it Here is how you would define and use a calculate_length function that has a reference to an object as a parameter instead of taking ownership of the value: [root@itoracle test]# cargo new references Created binary (application) `references` package [root@itoracle test]# cd references/ [root@itoracle references]# vim src/main.rs fn main() { let s1 = String::from("wa ka ka "); let _len = get_length(&s1); println!("The length of '{}' is {}",s1,_len); } fn get_length(ss: &String) -> usize{ ss.len() } [root@itoracle references]# cargo run Compiling references v0.1.0 (/usr/local/automng/src/rust/test/references) Finished dev [unoptimized + debuginfo] target(s) in 7.50s Running `target/debug/references` The length of 'wa ka ka ' is 9 These ampersands are references, and they allow you to refer to some value without taking ownership of it. ![]() The &s1 syntax lets us create a reference that refers to the value of s1 but does not own it. Because it does not own it, the value it points to will not be dropped when the reference goes out of scope. Likewise, the signature of the function uses & to indicate that the type of the parameter s is a reference. Let’s add some explanatory annotations: fn get_length(s: &String) -> usize { // s is a reference to a String s.len() } // Here, s goes out of scope. But because it does not have ownership of what // it refers to, nothing happens. 如果使用ownership方式求字符串长度,s1的可用范围就立即变化,引用的方式不会使变量可用范围发生变化, 以下是ownership的实现方式 fn main() { let s1 = String::from("wa ka ka "); let _len = get_length(s1); //基于ownership 时s1 move到了方法中,作用域虽然没有变,但后续不能再使用s1变量了 // println!("The length of '{}' is {}",s1,_len); println!("The length is {}",_len); } fn get_length(ss: String) -> usize{ ss.len() } |
Mutable References fn main() { let mut s1 = String::from("wa ka ka "); change(&mut s1); println!("s1={}",s1); } fn change(my_str: &mut String){ my_str.push_str("!!!"); } [root@itoracle references]# cargo run s1=wa ka ka !!! only one mutable reference to a particular piece of data in a particular scope First, we had to change s to be mut. Then we had to create a mutable reference with &mut s and accept a mutable reference with some_string: &mut String. But mutable references have one big restriction: you can have only one mutable reference to a particular piece of data in a particular scope. This code will fail: 下面代码是错误的 let mut s = String::from("hello"); let r1 = &mut s; let r2 = &mut s; println!("{}, {}", r1, r2); This restriction allows for mutation but in a very controlled fashion. It’s something that new Rustaceans struggle with, because most languages let you mutate whenever you’d like. The benefit of having this restriction is that Rust can prevent data races at compile time. A data raceis similar to a race condition and happens when these three behaviors occur: Two or more pointers access the same data at the same time. At least one of the pointers is being used to write to the data. There’s no mechanism being used to synchronize access to the data. Data races cause undefined behavior and can be difficult to diagnose and fix when you’re trying to track them down at runtime; Rust prevents this problem from happening because it won’t even compile code with data races! As always, we can use curly brackets to create a new scope, allowing for multiple mutable references, just not simultaneous ones: let mut s = String::from("hello"); { let r1 = &mut s; } // r1 goes out of scope here, so we can make a new reference with no problems. let r2 = &mut s; 下一次的可变引用,要建立在上一次的引用“不可用”的基础的上,下面的做法也可以 这里的 不可用,是指作用域可能还没消失,但运行时不可调用了,代码中不可使用了 fn main() { let mut s1 = String::from("wa ka ka "); change(&mut s1); println!("s1={}",s1); { let _r1 = &mut s1; } // r1 goes out of scope here, so we can make a new reference with no problems. let _r2 = &mut s1; change(&mut s1); //_r2在可变s1被第二次引用时不可再使用,后面不能再对_r2使用了 } fn change(my_str: &mut String){ my_str.push_str("!!!"); } 对于可变引用,如果代码中有一个以上的地方引用,那就表示至少有两处地方可以对该变量修改, 不符合rust可变变量只能有一处地方可修改的规则,编辑阶段就会报错。 一个可变变量同一作用域中只能有一处可用引用,新的引用生效时,已有引用失效,后续无法再次使用。 将已经用过一次的可变引用以方法参数传递时,之前对该可变变量的引用自动失效,只要后续不再使用之前的引用变量,代码可以正常运行; 如果是直接将可变引用第二次赋值到新变量,则是编译阶段报错。 fn main() { let mut s1 = String::from("wa ka ka "); change(&mut s1); println!("s1={}",s1); { let _r1 = &mut s1; } // r1 goes out of scope here, so we can make a new reference with no problems. let _r2 = &mut s1; change(&mut s1); let r3 = &s1; // no problem let r4 = &s1; // no problem let r5 = &mut s1; //前面还有引用(_r2)未失效,这里会报错 println!("{},{}",r3,r4); } fn change(my_str: &mut String){ my_str.push_str("!!!"); } error[E0502]: cannot borrow `s1` as mutable because it is also borrowed as immutable --> src/main.rs:14:14 | 12 | let r3 = &s1; // no problem | --- immutable borrow occurs here 13 | let r4 = &s1; // no problem 14 | let r5 = &mut s1; | ^^^^^^^ mutable borrow occurs here 15 | println!("{},{}",r3,r4); | -- immutable borrow later used here error: aborting due to previous error For more information about this error, try `rustc --explain E0502`. error: Could not compile `references`. |
字符串操作
fn first_word(s: &String) -> usize { let bytes = s.as_bytes(); for (i, &item) in bytes.iter().enumerate() { if item == b' ' { return i; } } s.len() } Because we need to go through the String element by element and check whether a value is a space, we’ll convert our String to an array of bytes using the as_bytes method
For now, know that iter is a method that returns each element in a collection and that enumerate wraps the result of iter and returns each element as part of a tuple instead. The first element of the tuple returned from enumerate is the index, and the second element is a reference to the element. This is a bit more convenient than calculating the index ourselves. Because the enumerate method returns a tuple, we can use patterns to destructure that tuple, just like everywhere else in Rust. So in the for loop, we specify a pattern that has i for the index in the tuple and &item for the single byte in the tuple. Because we get a reference to the element from .iter().enumerate(), we use & in the pattern. Inside the for loop, we search for the byte that represents the space by using the byte literal syntax. If we find a space, we return the position. Otherwise, we return the length of the string by using s.len()
fn main() { let mut s = String::from("hello world"); let _word = first_word(&s); // word will get the value 5 println!("{},{}",s,_word); s.clear(); // this empties the String, making it equal to "" // word still has the value 5 here, but there's no more string that // we could meaningfully use the value 5 with. word is now totally invalid! } fn first_word(s: &String) -> usize { let bytes = s.as_bytes(); for (i, &item) in bytes.iter().enumerate() { if item == b' ' { return i; } } s.len() }
下面的代码是正确的,只需要更换一下对切片使用的位置
pub fn test_s2(){ let mut s = String::from("hello world"); let word = first_word2(&s); println!("the first word is: {}", word); s.clear(); // right! } //传入原字符的引用,原字符没有ownership,返回原字符的切片 fn first_word2(s: &String) -> &str { let bytes = s.as_bytes(); for (i, &item) in bytes.iter().enumerate() { if item == b' ' { return &s[..i]; } } &s[..] }