변수, 상수
러스트의 기본 변수는 불변성이다. 이때문에 다음과 같은 코드를 컴파일하면 불변성 변수에 재할당으로 인해 에러가 발생한다.
1
2
3
4
5
6
7
|
fn main() {
let x = 5;
println!("The value of x is: {}", x);
x = 6;
println!("The value of x is: {}", x);
}
|
cs |
변수와 상수의 차이
상수는 불변성 그 자체가. 따라서 mut를 허용하지 않는다. 또 한 let 대신 const를 사용해야 한며 값을 유형을 선언해야 한다. 또 한 상수는 사진이 선언되 있는 영역 냉에서 프로그램이 실행되는 시간 동안 항상 유효하다.
shadowing
mut로 선언한 변수가 아닌 경우 러스트는 shadowing을 지원한다. Shadowing은 이전에 선언한 변수와 같은 이름의 새 변수를 선언할 수 있는 기능이다.
1
2
3
4
5
|
fn main() {
let spaces = " ";
let spaces = spaces.len();
}
|
cs |
위 같은 코드가 가능한 이유는 shadowing 때문이다. 두 개의 spaces는 그저 이름만 같을 뿐 서로 다른 유형의 변수이다.
데이터 타입
러스트에서 데이터 타입은 크게 스칼라와 컴파운드 두 종류로 나뉘어진다.
러스트는 강타입 언어이기 때문에 컴파일 시에 타입을 지정해 줘야 한다. 통상적으로 컴파일러는 사용하는 값에 따라 타입을 추측한다. 하지만 아래와 같이 타입의 선택 폭이 넓다면 타입을 면시해야 한다.
1
|
let guess: u32 = "42".parse().expect("Not a number");
|
cs |
스칼라 타입
스칼라 타입은 크게 정수형, 부동 소수점 숫자, boolean, 문자로 나뉘어져 있다.
정수형
Length(bit) | Signed | Unsigned |
8 | i8 | u8 |
16 | i16 | u16 |
32 | i32 | u32 |
64 | i64 | u64 |
arch | isize | usize |
isize, usize는 프로그램이 동작하는 컴퓨터 환경에 따라 길이를 결정한다. 환경이 32비트면 32bit를, 64비트이면 64bit를 갖는다. arch는 일반적으로 일부 콜렉션 타입의 색인에 사용된다.
정수는 실제 정수 뿐만 아니라 다음과 같은 정수형 리터럴을 사용할 수 있다. '_'는 시각적 구분을 위해 사용된다.
Number Literals | Example |
Decimal | 98_222 |
Hex | 0xff |
Octal | 0o77 |
Binary | 0b111_0000 |
Byte(u8 only) | b'A' |
부동 소수점
Length(bit) | Type |
32 | f32 |
64 | f64 |
Boolean
true, false
문자 타입
러스트의 char는 작은따옴표를 사용해 표기한다. char는 Unicode Scalar를 사용하기 때문에 한국어, 중국어, 이모티콘 등 ASCII보다 더 많은 표현을 할 수 있다.
복합 타입
복합 타입은 다른 타입의 다양한 값들을 하나의 타입으로 묶을 수 있다. 러스트는 튜플, 배열 총 두 개의 기본 타입을 기지고 있다.
튜플
튜플은 다양한 타입의 멸 개 숫자를 집합시켜 하나의 복합 타입을 만든다.
1
2
3
4
5
6
7
|
fn main() {
let tup: (i32, f64, u8) = (500, 6.4, 1);
// x = 500, y = 6.4, z = 1
let (x, y, z) = tup;
println!("{}, {}, {}", x, y, z);
}
|
cs |
배열
1
2
3
4
5
6
|
fn main() {
let arr = [1, 2, 3];
let first = arr[0];
println!("{}", first);
}
|
cs |
만약 배열에서 유효하지 않은 배열 요소에 접근한다면 컴파일 시에 아무런 에러도 발생시키지 않는다. 대신 프로그램 실행 중에 에러가 발생하고 비정상 종료된다.
색인을 사용해 요소에 접근할 경우 러스트는 지정한 색인이 배열 길이보다 작은지 확인한다. 색인이 길이보다 길면 러스트는 프로그램 오류와 함께 종료 된다. 이런 상황을 패닉(panic)이라 한다.
많은 저수준 언어들이 이런 타입의 검사를 수행하지 않아 유효하지 않은 메모리에 액세스할 수 있다. 반면 러스트는 즉시 종료를 해 이런 오류로부터 사용자를 보호한다.
함수
러스트는 함수의 위치를 신경쓰지 않는다. 즉 코드상 앞에 있는 함수에서 해당 함수보다 뒤에 있는 함수를 사용할 수 있다.
함수는 'fn' 키워드를 사용해 정의한다.
1
2
3
|
fn print_position(x: i32, y: i32) {
println!("{}, {}", x, y);
}
|
cs |
함수가 반환값을 갖는 경우 다음과 같이 사용된다.
1
2
3
4
5
6
7
8
|
fn main() {
println!("{}", plus_two_number(1, 2));
}
fn plus_two_number(x: i32, y: i32) -> i32 {
x + y
}
|
cs |
구문과 표현식
구문은 어떤 명령들의 나열로 값을 반환하지 않는 동작을 수행한다.
표현식은 결과값을 산출한다.
표현식은 구문의 일부일 수 있다. 함수를 호출하는 것, 매크로를 호출하는 것 등이 표현식이다. 또 한 표현식의 종결은 ';'으로 끝나지 않는다.
1
2
3
4
5
6
7
8
|
fn main() {
// {}부분이 표현식
// 아래의 표현식은 4를 반환
let y = {
let x = 3;
x + 1
};
}
|
cs |
반복문
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
fn main() {
// 무한 루프
loop {}
let number = 3;
while number != 0 {}
// for를 사용한 콜렉션 순환
let a = [10, 20, 30];
for element in a.iter() {}
// 1~3까지 순환, rev(): 역순
for number in (1..4).rev() {
println!("{}", number);
}
}
|
cs |
조건문
1
2
3
4
5
6
7
8
9
10
11
12
|
fn main() {
let number = 3;
if number < 3 {
println!("number is smaller than three");
} else if number > 3 {
println!("number is larger than three");
} else {
println!("number is three");
}
}
|
cs |
조건문은 표현식이다. 따라서 다음과 같이 let 구문에서 조건문을 사용할 수 있다.
1
2
3
4
5
6
7
8
|
fn main() {
let condition = true;
let number = if condition {
5
} else {
6
};
}
|
cs |
하지만 조건 문 내에 반환값의 타입이 서로 다르다면 동작 되지 않는다. 컴파일 타임에 타입을 지정할 수 없기 때문이다.
1
2
3
4
5
6
7
8
9
|
fn main() {
let condition = true;
// error[E0308]: if and else have incompatible types
let number = if condition {
5
} else {
'6'
};
}
|
cs |