일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | |||
5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 20 | 21 | 22 | 23 | 24 | 25 |
26 | 27 | 28 | 29 | 30 | 31 |
- Crawling
- 레벨 1
- socket
- Nestjs
- TCP
- 가천대
- 쉬운 문제
- ip
- BFS
- type challenge
- 그래프
- javascript
- 프로그래머스 레벨 2
- 자바스크립트
- dp
- 수학
- Algorithm
- dfs
- Node.js
- 알고리즘
- 타입 챌린지
- HTTP 완벽 가이드
- typescript
- 백준
- 타입스크립트
- 프로그래머스
- 소켓
- 크롤링
- HTTP
- 문자열
- Today
- Total
kakasoo
7. 참조와 사용 본문
소유권이 이전될 때의 문제
use std::io; // 어떤 외부 크레이트를 사용할지는 use로 명시 가능하다.
fn main() {
let mut input = String::new();
some_fn(input);
io::stdin().read_line(&mut input); // (1)
let mars_weight = calcuate_weight_on_mars(100.0);
println!("weight on Mars: {}kg", mars_weight);
}
fn calcuate_weight_on_mars (weight: f32) -> f32 {
(weight / 9.81) * 3.711
}
fn some_fn (s:String) {}
이 코드에서 (1) 구간에는 에러가 났다.
이유는 이 라인에 도달하기 전 input이 some_fn의 파라미터로 전달되어 소유권이 이전되었기 때문이다.
main에서는 더 이상 input 변수의 값에 접근할 수 없으니 그 다음 함수에게 input을 전달할 수 없는 것이다.
소유권은 효과적으로 이중 해제 문제를 해결하는 듯 하지만 조금만 생각해봐도 이상하다는 것을 알 수 있다.
함수에 파라미터로 변수를 전달하여 그 값을 돌려 받는 것은 모든 언어에서 자연스러운 일인데 여기서는 못 쓴다?
참조 ( Reference )
러스트에서는 이런 걸 ‘참조’ 라는 이름으로 따로 부르는데, 소유권 없이 값을 참조하는 방법을 의미한다.
방법은 매우 간단하다.
fn some_fn (s: &String) {}
함수의 시그니처를 문자열의 소유권을 취하는 대신 “문자열을 참조할 수 있도록” 파라미터에 & 기호를 추가한다.
이러면 변수 s가 범위를 벗어나도 문자열은 제거되지 않는데, 변수 s가 문자열의 소유자가 아니기 때문이다.
이런 식으로 러스트에서는 매개변수를 참조로 전달하는 것을 차용이라고 하는데,
이렇게 소유권을 전달하는 경우와 값 ( = 여기서는 주소를 의미한다 ) 만 빌려주는 것을 엄격히 분리하고 있다.
use std::io; // 어떤 외부 크레이트를 사용할지는 use로 명시 가능하다.
fn main() {
let mut input = String::new(); // 구조체를 사용하여 빈 문자열을 생성한다.
some_fn(&input);
io::stdin().read_line(&mut input); // 주소를 참조할 수 있도록 &mut로 명시한다.
let mars_weight = calcuate_weight_on_mars(100.0);
println!("weight on Mars: {}kg", mars_weight);
}
fn calcuate_weight_on_mars (weight: f32) -> f32 {
(weight / 9.81) * 3.711
}
fn some_fn (s: &String) {}
마찬가지로 전달하는 쪽에서도 & 기호를 붙여서 참조를 전달하는 것이라고 알려주어야 한다.
일반적인 변수처럼 참조는 기본적으로 변경이 불가능하다.
fn some_fn (s: &String) {
s.push_str('s'); // mismatched types expected `&str`, found `char`
}
만약 some_fn에서 s를 받아 값을 변경하고자 하면 rustc 에러가 난다.
fn some_fn (s: &mut String) { // 가변 참조
s.push_str('s');
}
이렇게 파라미터에도 &mut으로 명시해야 변경 가능한, 가변 참조 파라미터임을 알려줄 수 있다.
하지만 이렇게 하면 이제 다시 위에 함수 부분에서 에러가 난다.
use std::io;
fn main() {
let mut input = String::new();
some_fn(& input); // ERROR, 여기도 '&mut'로 변경해야 한다.
io::stdin().read_line(&mut input);
let mars_weight = calcuate_weight_on_mars(100.0);
println!("weight on Mars: {}kg", mars_weight);
}
fn calcuate_weight_on_mars (weight: f32) -> f32 {
(weight / 9.81) * 3.711
}
fn some_fn (s: &mut String) {
s.push_str("s");
}
가변과 불변 차용을 혼용할 때의 케이스
fn main() {
let input = String::new();
let mut s1 = &mut input; // 가변으로 차용
let s2 = &input; // 불변으로 차용할 때 ERROR 발생
}
앞서 가변으로 차용한 걸 다시 불변으로 차용하면 에러가 발생한다.
fn main() {
let input = String::new();
let mut s1 = &mut input; // 가변으로 차용
let mut s2 = &mut input; // 가변으로 차용할 때 ERROR 발생
}
동시에 두 개의 가변 차용을 하는 것도 에러 케이스이다.
동시에 값을 변경할 수 있는 변수가 2개 있다는 것은 시스템을 불안정하게 만들기 때문이다.
불변 차용은 개수 상관없이 원하는 만큼 할 수 있다면 가변 차용은 단일 가변 차용만 가능하다는 것을 알아야 한다.
불편하다고 느낄 수 있지만 이 방식은 데이터 레이스가 일어날 일이 없다는 것을 완벽하게 보장한다.
fn main() {
let mut input = String::new();
let s1 = & input; // 가변 변수를 불변으로 차용하지만 어차피 값이 이전 코드에 값 변경이 없었으므로 허용
let s2 = & input; // 가변 변수를 불변으로 차용하지만 어차피 값이 이전 코드에 값 변경이 없었으므로 허용
some_fn(&mut input);
}
또한 컴파일러는 영리하게도, 가변 변수를 불변으로 차용하더라도 값이 변경된 적이 없었다면 허용해준다.
fn main() {
let mut input = String::new();
some_fn(&mut input);
let s1 = & input; // ERROR
let s2 = & input;
}
만약 가변 변수를 선언 후 다른 함수에 가변 참조로써 전달하고 그 다음 값을 참조한 다른 변수를 선언하면 에러다.
이 예제 코드들은 러스트 컴파일러가 얼마나 영리한지 보여준다.
입출력 예제 코드
use std::io; // 어떤 외부 크레이트를 사용할지는 use로 명시 가능하다.
fn main() {
let mut input = String::new(); // 구조체를 사용하여 빈 문자열을 생성한다.
io::stdin().read_line(&mut input); // 주소를 참조할 수 있도록 &mut로 명시한다.
println!("Input: {}", input);
}
input을 입력받아 출력하는 예제 코드.
'프로그래밍 > Rust' 카테고리의 다른 글
6. 소유권 (Ownership) (0) | 2024.03.30 |
---|---|
5. 러스트 프로그래밍 시작 - 사용자 입력 받기 (1) | 2024.03.24 |
4. 러스트 프로그래밍 시작 - 함수 작성하기 (0) | 2024.03.24 |
3. 러스트 매크로 (0) | 2024.03.24 |
2. vscode에서 러스트 디버깅 하는 법 (0) | 2024.03.24 |