3.2. 데이터 타입
러스트에는 기본적으로 두 가지 유형의 타입이 있다. 스칼라와, 복합 타입이다.
러스트는 기본적으로 정적 타입 언어이다.
즉, 모든 변수의 타입은 컴파일 시점에 정해져 있어야만 한다.
기본값이 있는 경우가 아니고 여러 가지가 나올 수 있는 결과에 대해서는 반드시 타입 명시를 해야만 한다.
2. 추리 게임에서 guess를 parse할 때는 u32라는 것을 명시해줬던 것을 떠올리라.
스칼라 타입
정수형
부호가 있으면 i, 없으면 u를 사용한다.
길이는 8,16,32,64,128,arch가 허용된다.
arch는 컴퓨터 환경의 아키텍처에 따라 결정된다.
arch의 경우에는 isize
, usize
로 표현한다.
보통 32나 64가 되는데, 그걸 러스트가 알아서 찾아주는 것이다.
98_222 // 98222를 쉽게 보는 것
0xff // 16진수 표현
0o77 // 8진수 표현
0b1111 // 2진수 표현
b'A' // u8을 통한 바이트 표현(흔히 글자)
u8의 경우 0~ 255의 값을 가질 수 있는데, 이를 넘길 경우엔 오버플로우가 발생한다.
이때 panic이라 하여 에러를 발생하며 프로그램이 종료된다.
디버그 모드에서는 이러한 검사를 컴파일 단계에서 수행한다.
릴리즈 모드에서는 이를 수행하지 않는다.
대신 오버플로우 발생 시 2의 보수 감싸기를 수행한다.
넘는 값이 등장할 경우, 가령 256 -> 0 같은 식으로 최소값으로 돌아가는 방식을 말한다.
이는 의도되지 않는 동작이므로 보통 에러로 간주하는 편이 좋다.
이를 해결하는 방식에는 다음과 같은 것들이 있다.
- wrapping_add와 같이 감싸기 동작
- checked_* 메서드를 통해 오버 플로우 발생 시 None 값 반환
- overflowing_* 를 통해 오버플로우 발생 불린 값 반환
- saturating_* 를 통해 값을 제한
부동 소수점 타입
소수 타입에는 f32, f64가 있으며, 기본은 f64이다.
정밀하면서 성능은 비슷하기 때문이다.
모든 소수에는 부호가 있다.
수치 연산
사칙연산이 당연히 가능하다.
정수 나눗셈은 버림을 수행한다.
더 자세한 연산 기호에 대해서는 21.2. B - 연산자와 기호를 참조하도록 한다.
부울린 타입
bool 값은 true/false로 1바이트의 크기를 가진다.
https://stackoverflow.com/questions/4626815/why-is-a-boolean-1-byte-and-not-1-bit-of-size
cpu는 1바이트 이하의 크기를 계산할 수 없기에 어쩔 수 없다.
문자 타입
char은 기본적인 문자 타입이다.
특이하게도 보통은 1바이트인 다른 언어와 달리, 러스트에서는 4바이트의 크기를 갖는다.
즉, 아스키코드보다 넓은 범위를 커버할 수 있으며 이모티콘까지도 담을 수 있다.
그러나 유니코드와는 완벽히 맞지 않으며, 이것은 8.2. 문자열에 UTF-8 텍스트 저장하기에서 자세히 보게 될 것이다.
복합 타입
이것이 러스트의 두번째 큰 타입 유형이며, 여러 값을 하나의 타입으로 묶을 수 있다.
튜플 타입
파이썬의 튜플과 비슷하게, 한번 만들면 값을 변경할 수 없다.
고정된 길이를 갖는다는 편이 정확할 것이다.
let tup: (i32, f64, u8) = (500, 3.4, 1);
이렇게 내용물의 타입은 서로 달라도 상관없다.
또한 타입을 명시하지 않아도 상관 없다.
let (x,y,z) = tup;
이렇게 값을 해체시켜 사용할 수도 있다.
let one = tup.0;
let two = tup.1;
let three = tup.2;
위와 같이 인덱스를 통해 각 값을 꺼내오는 것도 가능하다.
아무것도 없는 튜플은 유닛(unit)이라고 특별히 이름붙인다.
이 값은 ()로 작성되고, 비어있음을 나타낸다.
표현식이 어떤 값도 반환하지 않는다면 유닛 값을 반환한다.
배열 타입
흔히 아는 array 방식을 사용할 수도 있다.
하지만 튜플과는 다르게 원소가 모두 같은 타입이어야만 한다.
튜플과 같이 고정된 길이를 가지게 된다.
let a = [1,2,3,4,5];
힙보다 스택에 데이터를 저장하거나, 고정된 개수의 요소로 값을 다룰 때 유용하다.
하지만 배열은 표준 라이브러리에서 제공하며 유연하게 크기를 조절할 수 있는 벡터처럼 유연하지 않다.
개수가 바뀌지 않는다면, 배열은 훨씬 유용하다.
let a[i32; 5] = [1,2,3,4,5];
이렇게 타입과 개수를 명시하여 적을 수 있다.
let a = [4; 5];
한 값으로 채워지게 배열을 초기화하고 싶다면 이렇게 쓴다.
위 구문은 4가 5개 채워진 배열을 생성할 것이다.
배열의 원소에 접근할 때는 다른 언어와 마찬가지로 []를 사용한다.
이러한 코드에서 의도적으로 인덱스를 넘는 값을 넣는다면
이러한 에러를 볼 수 있다.
런타임 에러로 패닉이 일어났다.
이는 저수준 언어 중에서는 특이한 케이스로, 대부분의 저수준 언어는 인덱스를 넘겨 유효하지 않은 메모리에 접근하는 것을 허용한다.