이 시리즈는 Rust Book을 공부하고 정리한 문서입니다. 댓글로 많은 조언 부탁드립니다.
Rust Book: https://doc.rust-lang.org/book/
address를 통해 값을 접근할 수 있는 방법.
값을 참고만 하되 오너십을 갖지는 않는 방법 : &
사용
&String s
pointing atString s1
함수 파라미터에 오너십을 넘기지 않고 값만 넘기는 것. (We call having references as function parameters borrowing.)
&s1
(변수명: &타입)
으로 정의fn main() {
let s1 = String::from("hello");
let len = calculate_length(&s1);
println!("The length of '{}' is {}.", s1, len);
}
fn calculate_length(s: &String) -> usize {
s.len()
}
s1은 calculate_length() 로 넘겼음에도 불구하고 해당 함수가 종료되어도 drop 되지 않는다.
fn main() {
let s = String::from("hello");
change(&s);
}
fn change(some_string: &String) {
some_string.push_str(", world");
}
cannot borrow immutable borrowed content
*some_string
as mutable
위의 코드 처럼 borrow 된 값은 변경이 안된다. Mutable reference로 선언해야한다.
String을 mut으로 선언해야하고 전달할때도 &mut
으로 전달 해야한다.
fn main() {
let mut s = String::from("hello");
change(&mut s);
}
fn change(some_string: &mut String) {
some_string.push_str(", world");
}
한 변수의 mutable reference는 동일 스코프에 딱 하나만 가질 수 있다. 아래 같은 코드는 컴파일 에러 발생. 이는 Data Race를 방지하기 위해서이다.
let mut s = String::from("hello");
let r1 = &mut s;
let r2 = &mut s;
Data Race는 다음과 같은 조건에서 발생할 수 있다. Rust는 여러가지 제약을 통해 이를 컴파일 타임에 방지한다.
다음과같이 한 스코프 당 하나의 &mut s 가 있는경우는 문제 없다.
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;
하지만 같은 스코프에 mutable 과 immutable reference 를 동시에 사용하는 경우는 에러가 발생한다. 반면 immutable reference 를 여러개 사용하는 것은 문제가 없는데 이는 데이터를 변경하는것이 애초에 불가능 하기 때문이다.
let mut s = String::from("hello");
let r1 = &s; // no problem
let r2 = &s; // no problem
let r3 = &mut s; // BIG PROBLEM
let mut s = String::from("hello");
let r1 = &mut s;
r1.push_str(", world");
println!("{}", s);
hello world 출력
이런 경우 에러 안남. s 출력 됨.
fn main() {
let mut s = String::from("hello");
let mut r1 = &mut s;
let r2 = &mut r1;
r2.push_str(", world2");
println!("{} ", r1);
println!("{} ", r2);
println!("{} ", s);
}
error[E0502]: cannot borrow `r1` as immutable because it is also borrowed as mutable --> src/main.rs:7:21 | 5 | let r2 = &mut r1; | ------- mutable borrow occurs here 6 | r2.push_str(", world2"); 7 | println!("{} ", r1); | ^^ immutable borrow occurs here 8 | println!("{} ", r2); | -- mutable 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
.
print 순서를 아래와 같이 바꾸면 컴파일 성공한다.
각 변수에 대해서 아래 `|` 으로 표기한 범위 처럼 스코프가 생성되는듯 하다. (?)
```rust
fn main() {
| let mut s = String::from("hello");
|
| | let mut r1 = &mut s;
| | | let r2 = &mut r1;
| | | r2.push_str(", world2");
| | | println!("{} ", r2);
| | println!("{} ", r1);
| println!("{} ", s);
}
메모리가 해제된 포인터를 댕글링 포인터라고 한다. 마찬가지로 레퍼런스도 댕글링 레퍼런스가 있다. 스코프를 벗어나 무효화(Invalid)된 변수의 레퍼런스를 리턴하는것이다. 레퍼런스의 변수는 항상 Valid 해야한다. Rust는 이런 댕글링 레퍼런스를 컴파일타임에 찾아준다.
fn main() {
let reference_to_nothing = dangle();
}
fn dangle() -> &String {
let s = String::from("hello");
&s
}
에러 메시지는 다음과 같다. (lifetime 10장 참고)
error[E0106]: missing lifetime specifier
이럴때는 &s
가 아닌 s
를 반환 하는게 좋다.