소유권이란 무엇인가?
fn main() {
let s = String::from("hello"); // s는 문자열의 소유자
takes_ownership(s); // s의 값이 이동됨
// println!("{}", s); // 오류: s는 이미 이동됨
}
fn takes_ownership(some_string: String) {
println!("{}", some_string);
} // some_string이 스코프를 벗어나고 `drop`이 호출됨
소유권 규칙 3가지
fn main() {
let x = 5;
let y = x; // 복사
println!("x = {}, y = {}", x, y); // 둘 다 사용 가능
let s1 = String::from("hello");
let s2 = s1; // 이동
// println!("{}", s1); // 오류: s1은 이미 이동됨
println!("{}", s2); // 정상 작동
}
변수의 스코프
fn main() {
{ // s는 유효하지 않음
let s = "hello"; // s는 이제부터 유효
println!("{}", s);
} // 이 스코프는 끝났고, s는 더 이상 유효하지 않음
// println!("{}", s); // 오류: s는 이 스코프에서 유효하지 않음
}
String 타입으로 이해하는 소유권
fn main() {
let s1 = String::from("hello");
let s2 = s1.clone(); // 깊은 복사
println!("s1 = {}, s2 = {}", s1, s2);
let x = 5;
let y = x; // 정수형은 Copy 트레이트를 구현하므로 복사됨
println!("x = {}, y = {}", x, y);
}
예제: 문자열 소유권 이동하기
let s1 = String::from("hello");
let s2 = s1; // s1의 소유권이 s2로 이동
// println!("{}", s1); // 오류: s1은 이미 이동됨
참조를 사용하는 이유
fn main() {
let s1 = String::from("hello");
let len = calculate_length(&s1); // s1의 참조를 전달
println!("길이는 {}입니다.", len);
println!("s1은 여전히 사용 가능: {}", s1);
}
fn calculate_length(s: &String) -> usize {
s.len()
}
가변 참조와 불변 참조
fn main() {
let mut s = String::from("hello");
let r1 = &s; // 불변 참조 ok
let r2 = &s; // 불변 참조 ok
println!("{}, {}", r1, r2);
let r3 = &mut s; // 가변 참조
println!("{}", r3);
}
댕글링 참조 방지하기
fn main() {
let reference_to_nothing = dangle();
}
fn dangle() -> &String { // 컴파일 에러
let s = String::from("hello");
&s // s가 함수 끝에서 drop되어 댕글링 참조 발생
}
예제: 참조로 함수 매개변수 전달하기
fn calculate_length(s: &String) -> usize {
s.len()
}
let s1 = String::from("hello");
let len = calculate_length(&s1);
println!("The length of '{}' is {}.", s1, len);
문자열 슬라이스란?
let s = String::from("hello world");
let hello = &s[0..5]; // "hello"
let world = &s[6..11]; // "world"배열 슬라이스 사용법
let a = [1, 2, 3, 4, 5];
let slice = &a[1..3]; // [2, 3]슬라이스 매개변수 활용
문자열 슬라이스를 매개변수로 사용
&str 타입으로 String과 &str 모두 처리 가능
예제:
fn first_word(s: &str) -> &str {
let bytes = s.as_bytes();
for (i, &item) in bytes.iter().enumerate() {
if item == b' ' {
return &s[0..i];
}
}
&s[..]
}
// String이나 &str 모두 사용 가능
let my_string = String::from("hello world");
let word = first_word(&my_string);
let my_string_literal = "hello world";
let word = first_word(my_string_literal);
예제: 첫 번째 단어 찾기 함수 만들기
fn first_word(s: &str) -> &str {
let bytes = s.as_bytes();
for (i, &item) in bytes.iter().enumerate() {
if item == b' ' {
return &s[0..i];
}
}
&s[..]
}
스택과 힙의 차이점
let x = 5; // 스택에 저장
let y = [1, 2, 3]; // 고정 크기 배열도 스택에 저장let s = String::from("hello"); // 힙에 문자열 데이터 저장
let v = vec![1, 2, 3]; // 벡터도 힙에 저장Copy 트레이트와 Drop 트레이트
let x = 5;
let y = x; // x의 값이 y로 복사됨
println!("x = {}, y = {}", x, y); // 둘 다 사용 가능let s1 = String::from("hello");
let s2 = s1; // s1의 소유권이 s2로 이동
// println!("{}", s1); // 오류! s1은 이미 이동됨소유권과 함수
함수에 값 전달 시 소유권 이동
fn main() {
let s = String::from("hello");
takes_ownership(s); // s의 소유권이 함수로 이동
// println!("{}", s); // 오류! s는 이미 이동됨
let x = 5;
makes_copy(x); // x는 Copy 타입이라 복사됨
println!("{}", x); // 정상 동작
}
fn takes_ownership(s: String) {
println!("{}", s);
}
fn makes_copy(i: i32) {
println!("{}", i);
}
예제: Vector를 이용한 메모리 할당/해제
fn main() {
let mut v = Vec::new();
v.push(1);
v.push(2);
v.push(3);
// v의 소유권이 print_vector로 이동
print_vector(v);
// v는 이미 이동되어 사용할 수 없음
// println!("{:?}", v); // 오류!
}
fn print_vector(v: Vec<i32>) {
println!("Vector: {:?}", v);
} // v가 스코프를 벗어나면서 자동으로 메모리 해제
문자열 처리하기
fn main() {
let mut s = String::from("hello world");
let word = first_word(&s);
s.clear(); // 오류! word는 여전히 전체 문자열을 참조하고 있음
}
fn first_word(s: &String) -> &str {
let bytes = s.as_bytes();
for (i, &item) in bytes.iter().enumerate() {
if item == b' ' {
return &s[0..i];
}
}
&s[..]
}
벡터 다루기
fn main() {
let mut numbers = vec![1, 2, 3, 4, 5];
// 벡터의 가변 참조로 수정
modify_vector(&mut numbers);
println!("수정된 벡터: {:?}", numbers);
// 벡터의 불변 참조로 합계 계산
let sum = calculate_sum(&numbers);
println!("벡터의 합: {}", sum);
}
fn modify_vector(v: &mut Vec<i32>) {
v.push(6);
v[0] = 10;
}
fn calculate_sum(v: &Vec<i32>) -> i32 {
v.iter().sum()
}
소유권 이동 추적하기
fn main() {
let s1 = String::from("안녕하세요");
let s2 = s1; // s1의 소유권이 s2로 이동
// println!("{}", s1); // 오류! s1은 이미 이동됨
println!("{}", s2); // 정상 동작
let s3 = s2.clone(); // 깊은 복사로 새로운 소유권 생성
println!("s2: {}, s3: {}", s2, s3); // 둘 다 사용 가능
}
참조 규칙 적용하기
fn main() {
let mut data = String::from("테스트");
let r1 = &data; // 첫 번째 불변 참조
let r2 = &data; // 두 번째 불변 참조 (가능)
println!("{}, {}", r1, r2);
let r3 = &mut data; // 가변 참조 (r1, r2는 더 이상 사용되지 않으므로 가능)
r3.push_str("123");
println!("{}", r3);
}