Rust - 변수
Rust 문법
1. 변수와 가변성
Rust에서 변수는 기본적으로 불변형이다.
그렇다고 상수와는 또 다르다. 실질적인 예시를 아래와 같다.
1
2
3
4
5
6
fn main() {
let x = 5;
println!("The value of x is: {}", x);
x = 6;
println!("The value of x is: {}", x);
}
위 코드를 실행시키면 에러가 난다!
이는 위에서 말한 불변형과 관련이 있다.
위의 코드에서 x 변수에 다시 값을 주고 싶으면 mut를 붙여야한다.
1
2
3
4
5
6
fn main() {
let mut x = 5;
println!("The value of x is: {}", x);
x = 6;
println!("The value of x is: {}", x);
}
x 변수는 새로 값이 할당될 수 있다는 것을 알려주는 표시인 것이다. 이 mut 값이 없다면 x에 대한 새로운 값을 할당할 수 없다. 하지만 아래와 같은 경우는 가능하다.
1
2
3
4
5
6
fn main() {
let x = 5;
let x = x + 1;
let x = x * 2;
println!("The value of x is: {}", x);
}
이 부분은 실제 상수인 const와는 다른 부분인데, 원래 있던 변수를 사용한 값이라면 위와같이 재 할당이 가능하다. 이를 shadowing이라고 한다.
다만 아래와 같은 경우는 안된다.
1
2
3
4
5
fn main() {
let mut x = 5;
x = x.len();
println!("The value of x is: {}", x);
}
mut 지시어가 있다면 같은 이름을 사용하는 변수의 유형을 바꿀 수 없기 때문이다.
2. 데이터 타입
rust에서도 아래와 같이 타입을 명시하여 변수를 만들 수 있다.
1
2
3
fn main() {
let b: f32 = 3.0; // f32형태의 변수
}
타입을 명시하지 않는다면 Number literals나 값을 보고 유추하여 타입이 정해진다.
rust의 데이터 타입은 크게 두 종류가 있다.
1) 스칼라 타입
하나의 값으로 표현되는 타입을 스칼라 타입이라고 한다. 총 4가지가 있다.
a. 정수형
소수점이 없는 숫자로, 부호가 필요없을 때 값의 크기를 2배로 크게 쓸수있는 unsigned형 부호를 사용하는 signed 형이 있다.
Length | Signed | Unsigned |
---|---|---|
8-bit | i8 | u8 |
16-bit | i16 | u16 |
32-bit | i32 | u32 |
64-bit | i64 | u64 |
arch | isize | usize |
변수에 값을 대입할때 아래와 같이 접두사나 접미사를 통해서 입력하면 해당 type으로 인지시킬수 있다.
Number literals | Example |
---|---|
Decimal | 98_222 |
Hex | 0xff |
Octal | 0o77 |
Binary | 0b1111_0000 |
Byte (u8 only) | b'A' |
b. 부동소수점
총 두 가지 타입이 있다. f32와 f64가 그 두가지인데, 각각 32bit와 64bit의 크기를 가지며 기본 타입은 f64이다. 이는 둘다 대략 비슷한 성능에 f64가 좀 더 정밀하게 표현이 가능하기 때문이다. 아래는 그 표기의 예시이다.
1
2
3
4
fn main() {
let a = 2.0; // f64
let b: f32 = 3.0; // f32
}
c. boolean
다른 언어들처럼 true 혹은 false를 나타낼 수 있으며 아래와 같이 지정할 수 있다.
1
2
3
fn main() {
let a: bool = true;
}
d. 문자 타입
rust의 문자 타입인 char
는 Unicode Scalar를 표현하는 값이다. String 타입과는 다른 타입이기 때문에 주의해야하며 작은 따옴표로 아래와 같이 표기 할 수 있다.
1
2
3
4
fn main() {
let c = 'z';
let z = 'ℤ';
}
e 문자열 타입
한 개의 문자로 이루어진 문자 타입과는 다르게 문자열 타입은 여러 문자로 이루어져있다. 또한 한번 지정하면 변경할 수 없다. 문자열 타입은 아래와 같이 쓴다.
1
2
3
4
fn main() {
let s = "hello";
println!("{}", s);
}
아래와 같이 사용하기도 한다.
1
2
3
4
fn main() {
let s: &'static str = "hello";
println!("{}", s);
}
2) 복합 타입
a. 튜플
값들을 집합시켜서 만들 수 있는 타입이다. 일반적으로 몇 개의 숫자를 집합시켜 튜플로 만드는데 튜플에 포함되는 각 값의 타입이 동일할 필요는 없다. 아래는 그 예시이다.
1
2
3
fn main() {
let tup: (i32, f64, u8) = (500, 6.4, 1);
}
이렇게 만들어진 튜플을 해제하기 위해서는 패턴 매칭을 통해 튜플의 값을 해체하여 사용하면 된다.
1
2
3
4
5
6
7
fn main() {
let tup = (500, 6.4, 1);
let (x, y, z) = tup;
println!("The value of y is: {}", y);
}
혹은 원하는 값의 색인을 넣는 것으로 튜플 요소에 직접 접근할 수 있따. 예시는 아래와 같다.
1
2
3
4
5
6
7
8
9
fn main() {
let x: (i32, f64, u8) = (500, 6.4, 1);
let five_hundred = x.0;
let six_point_four = x.1;
let one = x.2;
}
b. 배열
여러 값들의 집합체를 배열이라고 한다. 튜플이랑 다른 점은 배열의 모든 요소는 같은 타입이어야하며 배열은 고정된 길이를 갖는다.
선언하는 방법은 아래와 같다.
1
2
3
fn main() {
let a = [1, 2, 3, 4, 5];
}
선언된 배열은 다른 언어에서 배열에 접근하듯이 index 값으로 접근이 가능하다.
1
2
3
4
5
6
fn main() {
let a = [1, 2, 3, 4, 5];
let first = a[0];
let second = a[1];
}
index 값은 0부터 시작하며 만약 유효하지 않은 값을 넣는다면 실행중에 에러가 발생하게 된다.
3) String 타입
문자열 타입에 대해서 언급했는데 왜 String 타입이 나오는지 의문스러울 것이다.
이는 문자열 타입의 경우 데이터를 바꿀수없고, 별도의 함수를 통해서 가공할 수 있는 반면 이 String 타입은 값을 바꿀수 있으며 힙 영역에 저장된다는 점이 다르기 때문에 별도의 타입으로 분류해두었다.
세부적인 내부 구조의 경우 소유권에 대해 포스팅할 때 추가적으로 포스팅할 예정이며 String 타입에 대해 정의는 아래와 같이 한다.
1
2
3
4
fn main() {
let mut s = String::from("hello");
println!("{}", s);
}