변수와 가변성
러스트에서 변수는 기본적으로 immutable이다. 코드를 읽고 쓸 때 값이 어디서 바뀔지 추적할 필요가 없도록 하기 위함이다. 변수를 가변으로 만들려면 앞에 mut
을 붙이면 된다.
fn main() {
let mut x = 5;
println!("The value of x is: {x}");
x = 6;
println!("The value of x is: {x}");
상수
상수는 항상 불변이므로 mut
를 사용할 수 없다. let
대신 const
를 사용한다.
관례적으로 단어 사이에 밑줄을 사용하고 모든 글자를 대문자로 쓴다.
const HOURS_IN_SECONDS: u32 = 60 * 60;
섀도잉
새 변수를 이전 변수명과 같은 이름으로 선언할 수 있다. 이 때 첫 번째 변수는 두 번째 변수에 의해 가려진다(shadowed). 두 번째 변수의 스코프가 끝날 때까지 첫 번째 변수는 가려진다.
fn main() {
let x = 5;
let x = x + 1;
{
let x = x + 1;
//7 출력
println!("The value of x in the inner scope is :{x}");
}
//6 출력
println!("The value of x is: {x}");
}
x는 처음에 5로 바인딩되고, 섀도잉에 의해 6으로 가려진다. 그리고 블록 안에서 다시 섀도잉에 의해 7로 가려진다. 7을 출력한 후 블록을 벗어났으므로, 블록 안에서 가려졌던 6이 다시 드러나게 된다.
섀도잉은 동일한 변수를 let
키워드로 새로 선언할 때 발생한다. let
키워드가 없다면, x
는 불변이므로 에러가 발생한다.
데이터 타입
러스트는 정적 타입(statically typed) 언어로, 모든 변수의 타입이 컴파일 시점에 정해져 있어야 한다.
컴파일러는 타입을 추측할 수 있지만, 여러 타입이 가능한 경우라면 타입을 반드시 명시해야 한다.
스칼라 타입
스칼라(scalar) 타입은 하나의 값을 표현하는 타입으로 정수, 부동소수점 숫자, 불리언, 문자 4가지가 있다.
정수형 리터럴 숫자 사이에 를 사용하여 시각적으로 구분할 수 있다. 예를 들어, 100000
은 100_000
과 동일하다.
러스트에서 char 타입은 4바이트 크기로 유니코드 값을 표현한다.
복합 타입
- 튜플
fn main() {
let tup = (500, 6.4, 1);
let (x, y, z) = tup; //구조 해체
}
튜플을 분리하는 것을 구조 해체(destructuring)이라 부른다.
아무 값도 없는 튜플은 유닛(unit)이라 부른다. 표현식이 어떤 값도 반환하지 않는다면 암묵적으로 유닛을 반환한다.
- 배열
동일한 타입으로 구성된 고정된 길이의 집합체
함수
러스트 코드는 스네이크 케이스(snake case), 즉 모든 글자를 소문자로 쓰고 밑줄로 단어를 구분하는 방식을 이용한다.
fn plus_one(x: i32) -> i32 {
x + 1
}
괄호 안에 들어가는 내용 x: i32
는 함수에 들어갈 매개변수이다. 매개변수의 이름은 x이고, 32비트 정수 형식이다. -> i32
는 반환값을 나타낸다. 32비트 정수형을 반환하는 함수이다.
다른 언어와 다르게 러스트에서는 반환할 때 return
키워드를 사용하지 않는다. 이는 구문과 표현식의 차이 때문이다.
- 구문(statement)은 어떤 동작을 수행하고 값을 반환하지 않는 명령이다.
- 표현식(Expression)은 결괏값을 평가한다.
fn main() {
let y = 6;
}
let y = 6;
은 구문이다. 함수 정의 자체도 구문이다.
fn main() {
let x = (let y = 6);
}
구문은 값을 반환하지 않으므로, 위 코드는 에러가 발생한다.
대부분의 코드는 표현식이다. let y = 6;
에서 6은 6이라는 값을 평가하는 표현식이다. 표현식은 구문의 일부일 수 있다. 표현식은 종결을 나타내는 세미콜론을 쓰지 않는다.
다시 함수를 살펴보자.
fn plus_one(x: i32) -> i32 {
x + 1
}
세미콜론을 쓰면 표현식이 구문으로 바뀌어 값을 반환하지 않게 된다. 함수는 암묵적으로 마지막 표현식값을 반환한다.
제어 흐름 - 조건문
if
fn main() {
let number = 3;
if number < 5 {
println!("condition was true");
} else {
println!("condition was false");
}
}
c언어에서는 0이란 정수로 참과 거짓을 판단할 수 있지만, 러스트에서는 이런 값을 자동으로 불리언으로 변환하지 않는다.
만약 else if 구조가 너무 복잡해지면 match 구조를 사용해 리팩토링하는 것이 좋다.
if는 표현식이므로 결과를 바로 변수에 할당할 수 있다.
let condition = true;
let number = if conditiion {5} else {6};
제어 흐름 - 반복문
loop
명시적으로 알려주기 전까지 무한 반복한다.
fn main() {
loop {
println!("again!");
}
}
break
와 continue
는 바로 바깥쪽 루프에 적용되는데, 루프 라벨(loop label)을 추가하면 특정 루프를 명시할 수 있다.
fn main() {
let mut count = 0;
'counting_up: loop {
println!("count = {count}");
let mut remaining = 10;
loop {
println!("remaining = {remaining}");
if remaining == 9 {
break;
}
if count == 2 {
break 'counting_up;
}
remaining -= 1;
}
count += 1;
}
println!("End count = {count}");
}
바깥쪽 루프는 0에서 2까지 카운트하고, 안족 루프는 10에서 9까지 거꾸로 카운팅한다. 첫 번째 브레이크는 안쪽 루프만 벗어난다. break
counting_up;` 구문은 바깥쪽 루프를 탈출한다.
실행 결과는 다음과 같다.
count = 0
remaining = 10
remaining = 9
count = 1
remaining = 10
remaining = 9
count = 2
remaining = 10
End count = 2
while
조건문이 true인 동안 계속 반복한다.
fn main() {
let mut number = 3;
while number != 0 {
println!("{number}!");
number -= 1;
}
println!("LIFTOFF!!!");
}
for
컬렉션의 각 아이템에 대해 임의의 코드를 수행한다.
fn main() {
let a = [10, 20, 30, 40, 50];
let mut index = 0;
while index < 5 {
println!("the value is: {}", a[index]);
index += 1;
}
'프로그래밍 > Rust' 카테고리의 다른 글
[Rust] 컬렉션 (0) | 2024.05.18 |
---|---|
[Rust]프로젝트 모듈 관리 (0) | 2024.05.17 |
[Rust] 열거형 (0) | 2024.05.17 |
[Rust] 구조체 (0) | 2024.05.16 |
[Rust] 소유권 (0) | 2024.05.16 |