{ // s는 유효하지 않습니다. 아직 선언이 안됐거든요.
let s = "hello"; // s는 이 지점부터 유효합니다.
// s를 가지고 뭔가 합니다.
} // 이 스코프는 이제 끝이므로, s는 더이상 유효하지 않습니다.
String 타입
스트링 리터럴은 불변!
String 타입의 인스턴스는 가변!
let mut s = String::from("hello");
s.push_str(", world!"); // push_str()은 해당 스트링 리터럴을 스트링에 붙여줍니다.
println!("{}", s); // 이 부분이 `hello, world!`를 출력할 겁니다.
메모리 할당
{
let s = String::from("hello"); // s는 여기서부터 유효합니다
// s를 가지고 뭔가 합니다
} // 이 스코프는 끝났고, s는 더 이상
// 유효하지 않습니다
변수와 데이터가 상호작용하는 방법: 이동(move)
정수에서는 값 의미. 복사가 일어남.
let x = 5;
let y = x;
String 타입에서는...
let s1 = String::from("hello");
let s2 = s1;
그럼 실제로 어떻게 되나?
let s1 = String::from("hello");
let s2 = s1;
println!("{}, world!", s1);
error[E0382]: use of moved value: `s1`
--> src/main.rs:4:27
|
3 | let s2 = s1;
| -- value moved here
4 | println!("{}, world!", s1);
| ^^ value used here after move
|
= note: move occurs because `s1` has type `std::string::String`,
which does not implement the `Copy` trait
let s2 = s1;이 실행되는 순간 값이 이동된다!!!
Rust는 실행 시간에 결코 자동으로깊은 복사(deep copy)를 하지 않음.
변수와 데이터가 상호작용하는 방법: 클론
let s1 = String::from("hello");
let s2 = s1.clone();
println!("s1 = {}, s2 = {}", s1, s2);
값을 복사하려면 명시적으로clone() 메소드를 호출한다.
성능이 느려질 수도 있음을 시각적으로 표시.
스택에만 있는 데이터: 복사
let x = 5;
let y = x;
println!("x = {}, y = {}", x, y);
컴파일 시간에 결정되어 있는 크기의 자료형은 스택에 모두 저장!
값이 박사되려면 Copy 트레잇을 갖고 있어야!
정수, 실수, 부울 등은 Copy 가능
Copy 가능한 데이터들의 튜플 역시 Copy 가능
소유권과 함수
fn main() {
let s = String::from("hello"); // s가 스코프 안으로 들어왔습니다.
takes_ownership(s); // s의 값이 함수 안으로 이동했습니다...
// ... 그리고 이제 더이상 유효하지 않습니다.
let x = 5; // x가 스코프 안으로 들어왔습니다.
makes_copy(x); // x가 함수 안으로 이동했습니다만,
// i32는 Copy가 되므로, x를 이후에 계속
// 사용해도 됩니다.
} // 여기서 x는 스코프 밖으로 나가고, s도 그 후 나갑니다. 하지만 s는 이미 이동되었으므로,
// 별다른 일이 발생하지 않습니다.
fn takes_ownership(some_string: String) { // some_string이 스코프 안으로 들어왔습니다.
println!("{}", some_string);
} // 여기서 some_string이 스코프 밖으로 벗어났고 `drop`이 호출됩니다. 메모리는
// 해제되었습니다.
fn makes_copy(some_integer: i32) { // some_integer이 스코프 안으로 들어왔습니다.
println!("{}", some_integer);
} // 여기서 some_integer가 스코프 밖으로 벗어났습니다. 별다른 일은 발생하지 않습니다.
와, 대박!! String 인스턴스를 함수로 넘기면 무효화된다고?
음... 그래서 참조자가 필요한 건가?
반환값과 스코프
fn main() {
let s1 = gives_ownership(); // gives_ownership은 반환값을 s1에게
// 이동시킵니다.
let s2 = String::from("hello"); // s2가 스코프 안에 들어왔습니다.
let s3 = takes_and_gives_back(s2); // s2는 takes_and_gives_back 안으로
// 이동되었고, 이 함수가 반환값을 s3으로도
// 이동시켰습니다.
} // 여기서 s3는 스코프 밖으로 벗어났으며 drop이 호출됩니다. s2는 스코프 밖으로
// 벗어났지만 이동되었으므로 아무 일도 일어나지 않습니다. s1은 스코프 밖으로
// 벗어나서 drop이 호출됩니다.
fn gives_ownership() -> String { // gives_ownership 함수가 반환 값을
// 호출한 쪽으로 이동시킵니다.
let some_string = String::from("hello"); // some_string이 스코프 안에 들어왔습니다.
some_string // some_string이 반환되고, 호출한 쪽의
// 함수로 이동됩니다.
}
// takes_and_gives_back 함수는 String을 하나 받아서 다른 하나를 반환합니다.
fn takes_and_gives_back(a_string: String) -> String { // a_string이 스코프
// 안으로 들어왔습니다.
a_string // a_string은 반환되고, 호출한 쪽의 함수로 이동됩니다.
}