-
보편적인 프로그래밍 개념 - 데이터 타입24년 11월 이전/Rust 2022. 3. 6. 20:00반응형
개발 환경
이 문서에서 진행한 필자의 개발 환경은 다음과 같다.
- desktop: macbook pro 13 2020
- cpu: Intel Core i7 4core
- memory: 32GB
- rustup v1.24.3
- cargo v1.58.0
이 문서는 여러분이 cargo가 설치되어 있다고 가정한다. 만약 cargo를 설치하지 않았다면, 이 문서를 참고하여 설치 및 설정을 진행하길 바란다.
cargo 설치가 되었다면 이번 장을 위한 프로젝트를 생성한다.
# 프로젝트 생성 $ cargo new --bin datatypes # 프로젝트 디렉토리로 이동 $ cd datatypes
데이터 타입
rust
에서의 모든 값들은 고정된 특정 "타입"을 갖는다. 이것은rust
는 컴파일 시에 모든 변수의 타입이 정해져야 함을 의미한다. 예를 들어서main.rs
를 다음과 같이 작성해보자.datatypes/src/main.rs
fn main() { let guess = "42".parse().expect("Not a number!"); println!("guess: {}", guess); }
이제 컴파일을 해보자.
$ cargo run Compiling datatypes v0.1.0 (/Users/gurumee/Workspace/today-i-learned/getting-started-rust-programming/ch03/datatypes) error[E0282]: type annotations needed --> src/main.rs:2:9 | 2 | let guess = "42".parse().expect("Not a number!"); | ^^^^^ consider giving `guess` a type For more information about this error, try `rustc --explain E0282`. error: could not compile `datatypes` due to previous error
이 경우, 변수
guess
의 타입이 확정되지 않아서 에러가 발생한다. 때문에 아래 코드처럼guess
의 타입을 확정하게끔main.rs
를 수정해야 한다.datatypes/src/main.rs
fn main() { let guess: u32 = "42".parse().expect("Not a number!"); println!("guess: {}", guess); }
컴파일을 실행해보면, 이전과 달리 정상적으로 수행되는 것을 확인할 수 있다.
$ cargo run Compiling datatypes v0.1.0 (/Users/gurumee/Workspace/today-i-learned/getting-started-rust-programming/ch03/datatypes) Finished dev [unoptimized + debuginfo] target(s) in 0.29s Running `target/debug/datatypes` guess: 42
rust
에서 데이터 타입은 크게 2가지로 아래와 같이 나눌 수 있다.- 스칼라 타입
- 복합 타입
이 문서에서는
rust
에서 지원하는 스칼라 타입과 간단한 복합 타입 2가지를 소개한다.스칼라 타입
스칼라 타입이란 하나의 값으로 표현되는 타입을 의미한다.
rust
에서 지원하는 스칼라 타입은 크게 다음과 같이 4가지로 분류할 수 있다.- 정수형
- 문자형
- 실수형
- bool
정수형
정수형 데이터 타입은 소수점이 없는 값들을 표현한다. 대표적으로
u32
,i32
등이 있다. 이들은 32비트 정수형을 의미한다.u
혹은i
뒤의 숫자는 데이터의 크기를 의미하며8
,16
,32
,64
가 있다. 정수형은 크게 다음과 같이 분류할 수 있다.Length Signed Unsigned 8 i8 u8 16 i16 u16 32 i32 u32 64 i64 u64 arch isize usize isize
,usize
는 컴퓨터 아키텍처에 알맞는 정수형 타입을 할당한다. 예를 들어 32bit 아키텍처라면u32
,i32
를 64bit 아키텍처라면u64
,i64
를 사용한다.i
와u
의 차이는 부호를 갖는 정수 여부를 의미한다.i
는 부호를 갖는 정수값을,u
는 부호를 갖지 않는 정수값 즉 0 이상의 정수를 표현한다. 각 타입이 표현할 수 있는 값의 범위는 다음과 같다.Data Type Range i8 -(2^7) ~ (2^7 - 1) i16 -(2^15) ~ (2^15 - 1) i32 -(2^31) ~ (2^31 - 1) i64 -(2^63) ~ (2^63 - 1) u8 0 ~ (2^8 - 1) u16 0 ~ (2^16 - 1) u32 0 ~ (2^32 - 1) u64 0 ~ (2^64 - 1) 기본적으로 다음과 같이 사용할 수 있다.
let i: i32 = -16; let u: u32 = 16;
정수형은 사칙 연산자를 사용할 수 있다.
main.rs
를 다음과 같이 수정해보자.datatypes/src/main.rs
fn main() { let sum = 5 + 10; let sub = 5 - 10; let mul = 5 * 10; let div = 5 / 10; println!("sum: {}, sub: {}, mul: {}, div: {}", sum, sub, mul, div); }
이제 컴파일 및 실행을 해보자. 결과 값을 예상할 수 있는가?
$ cargo run Compiling datatypes v0.1.0 (/Users/gurumee/Workspace/today-i-learned/getting-started-rust-programming/ch03/datatypes) Finished dev [unoptimized + debuginfo] target(s) in 0.29s Running `target/debug/datatypes` sum: 15, sub: -5, mul: 50, div: 0
참고! div는 왜 0이 나오나요?
일반적으로 5 / 10 = 0.5 이다. 근데 왜 div는 0이 나왔을까? 프로그래밍 세계에서는 정수형 / 정수형 = 정수형 이기 때문이다. 실수를 표현하고 싶다면 다음 절 "실수형"을 살펴보면 된다.실수형
실수형 데이터 타입은 소수점이 있는 실수를 표현한다.
IEEE-754
표준에 맞게 데이터 타입이 설계되었는데 복잡한 이야기는 빼도록 하자.rust
에서 실수형 데이터 타입은f32
,f64
가 있다. 정수형 데이터 타입과 동일하기 각각 32bit, 64bit 크기만큼의 실수를 표현해낼 수 있다. 우리는 거의 모든 상황에서f64
를 쓰면 된다. 다음과 같이 사용할 수 있다.let f: f64 = 2.5;
정수형과 마찬가지로 사칙 연산자를 사용할 수 있다. 이제
main.rs
를 다음과 같이 수정해보자.datatypes/src/main.rs
fn main() { let sum = 5.0 + 10.0; let sub = 5.0 - 10.0; let mul = 5.0 * 10.0; let div = 5.0 / 10.0; println!("sum: {}, sub: {}, mul: {}, div: {}", sum, sub, mul, div); }
이제 컴파일 및 실행해보자.
$ cargo run Compiling datatypes v0.1.0 (/Users/gurumee/Workspace/today-i-learned/getting-started-rust-programming/ch03/datatypes) Finished dev [unoptimized + debuginfo] target(s) in 0.26s Running `target/debug/datatypes` sum: 15, sub: -5, mul: 50, div: 0.5
참고! sum, sub, mul은 왜 정수형으로 표현되었나요?
기본적으로 rust에서는 실수형일지라도 5.0 처럼 정수로 표현할 수 있는 실수는 정수로 표현되기 때문이다.여기서 한 가지 주의할 점은 실수형과 실수형, 정수형과 정수형은 서로 사칙연산이 되지만 정수형과 실수형은 사칙연산이 불가하다.
main.rs
를 다음과 같이 수정해보라.datatypes/src/main.rs
fn main() { let sum = 5 + 10.0; let sub = 5 - 10.0; let mul = 5 * 10.0; let div = 5 / 10.0; println!("sum: {}, sub: {}, mul: {}, div: {}", sum, sub, mul, div); }
이제 정말 실행이 안되는지 컴파일 및 실행해보자.
$ cargo run Compiling datatypes v0.1.0 (/Users/gurumee/Workspace/today-i-learned/getting-started-rust-programming/ch03/datatypes) error[E0277]: cannot add a float to an integer --> src/main.rs:2:18 | 2 | let sum = 5 + 10.0; | ^ no implementation for `{integer} + {float}` | = help: the trait `Add<{float}>` is not implemented for `{integer}` error[E0277]: cannot subtract `{float}` from `{integer}` --> src/main.rs:3:18 | 3 | let sub = 5 - 10.0; | ^ no implementation for `{integer} - {float}` | = help: the trait `Sub<{float}>` is not implemented for `{integer}` error[E0277]: cannot multiply `{integer}` by `{float}` --> src/main.rs:4:18 | 4 | let mul = 5 * 10.0; | ^ no implementation for `{integer} * {float}` | = help: the trait `Mul<{float}>` is not implemented for `{integer}` error[E0277]: cannot divide `{integer}` by `{float}` --> src/main.rs:5:18 | 5 | let div = 5 / 10.0; | ^ no implementation for `{integer} / {float}` | = help: the trait `Div<{float}>` is not implemented for `{integer}` For more information about this error, try `rustc --explain E0277`. error: could not compile `datatypes` due to 4 previous errors
에러를 발생시키는 모든 구문을 확인할 수 있다. 모두 정수, 실수를 섞어서 사칙연산을 시도할 수 없다고 에러 문구를 출력하고 있다. 이럴 경우, 모두 실수, 정수로 맞춰주거나 강제로 타입 캐스팅을 해야 한다. 타입 캐스팅이란 컴파일 시 실제 타입 말고 개발자가 원하는 다른 타입으로 바꾸는 것을 의미한다. 다음과 같이 사용할 수 있다.
datatypes/src/main.rs
fn main() { let sum = 5 + 10.0 as u32; let sub = 5 - 10.0 as i64; let mul = 5 as f32 * 10.0; let div = 5 as f64 / 10.0; println!("sum: {}, sub: {}, mul: {}, div: {}", sum, sub, mul, div); }
이제 다시 컴파일 및 실행을 해보자. 정상적으로 실행되는 것을 확인할 수 있다.
$ cargo run Compiling datatypes v0.1.0 (/Users/gurumee/Workspace/today-i-learned/getting-started-rust-programming/ch03/datatypes) Finished dev [unoptimized + debuginfo] target(s) in 0.25s Running `target/debug/datatypes` sum: 15, sub: -5, mul: 50, div: 0.5
bool
bool
은 참, 거짓을 나타내는 스칼라 타입이다. 일반적으로 혼자 사용되는 것은 드물고 조건문 및 반복문과 함께 사용된다. 다음과 같이 사용할 수 있다.let b: bool = true; //false도 사용할 수 있다.
문자형
문자형 데이터 타입은 쉽게 생각해서 문자 한 개를 표현한다고 보면 된다.
rust
에서는ASCII
가 아닌Unicode Scalar
를 사용하는데, 문자형 타입인char
는 알파벳뿐 아니라 한글, 중국어, 이모티콘 등 여러 문자를 표현해낼 수 있다.기본적으로 다음과 같이 사용할 수 있다.
let c: char = 'Z';
우리가 이전에 썼던
"...."
은&str
이라는 데이터 타입이다. 이는 문자 타입인char
와 다르다는 것을 알아두자. 추후 다른 문서에서 "문자열 타입(&str
)"을 다루도록 하겠다.복합 타입
복합 타입은 다른 타입의 다양한 값들을 하나의 타입으로 묶는 타입을 의미한다.
rust
에서 기본적으로 제공되는 타입은 다음과 같다.- 튜플
- 배열
- 구조체
구조체는 다른 문서에서 다룰 것이며, 이 문서에서는 튜플과 배열만 다루도록 하겠다.
튜플
튜플은 다양한 데이터 타입을 나열한 것과 같다. 예를 들어 이렇게 사용될 수 있다.
let tup: (i32, f64, bool) = (500, 6.4, true);
한 번 튜플을 써보자.
main.rs
를 다음과 같이 수정한다.datatypes/src/main.rs
fn main() { let tup: (i32, f64, bool) = (500, 6.4, true); let x = tup.0; let y = tup.1; let z = tup.2; println!("x: {}, y: {}, z: {}", x, y, z); }
위 프로그램은 정수형, 실수형, bool 데이터 타입의 값을 갖는 튜플을 생성한 후 x, y, z에 각 값을 할당하여 출력하는 프로그램이다. 이제 컴파일 및 실행해보자.
$ cargo run Compiling datatypes v0.1.0 (/Users/gurumee/Workspace/today-i-learned/getting-started-rust-programming/ch03/datatypes) Finished dev [unoptimized + debuginfo] target(s) in 0.25s Running `target/debug/datatypes` x: 500, y: 6.4, z: true
튜플의 장점은 이런 식으로 "구조 분해"문법을 사용해서 간단하게 초기화가 가능하다는 것이다.
datatypes/src/main.rs
fn main() { let tup: (i32, f64, bool) = (500, 6.4, true); let (x, y, z) = tup; // 구조 분해 문법! println!("x: {}, y: {}, z: {}", x, y, z); }
결과는 위와 동일하다.
배열
튜플이 다른 타입의 값들을 하나로 묶어주는 타입이라면, 배열은 같은 타입의 값들을 하나로 묶어주는 타입이다. 이런 식으로 사용이 가능하다.
let arr = [1, 2, 3];
이런 식으로 접근이 가능하다.
let arr = [1, 2, 3]; let x = a[0]; let y = a[1]; let z = a[2];
첫 원소가 0으로 접근하는 것에 유의하자. 프로그래밍에서는 "인덱스로 접근한다"라고 표현하는데 첫 원소를 0부터 센다고 보면 된다. 배열 사용시 주의할 점은 배열 크기를 넘는 인덱스를 사용하면 에러가 발생한다는 것이다.
main.rs
를 다음과 같이 수정한다.datatypes/src/main.rs
fn main() { let arr = [1, 2, 3]; println!("index: 3, arr[3]: {}", arr[3]); }
그 후 컴파일 및 실행해보자.
$ cargo run Compiling datatypes v0.1.0 (/Users/gurumee/Workspace/today-i-learned/getting-started-rust-programming/ch03/datatypes) error: this operation will panic at runtime --> src/main.rs:3:38 | 3 | println!("index: 3, arr[3]: {}", arr[3]); | ^^^^^^ index out of bounds: the length is 3 but the index is 3 | = note: `#[deny(unconditional_panic)]` on by default error: could not compile `datatypes` due to previous error
그럼 에러 문구에서 배열 길이는 3인데 인덱스는 3으로 접근하면 안된다는 에러가 출력된다. 위에서 언급했던것 처럼 인덱스는 0부터 세므로 길이가 3이면, 접근할 수 있덱스는 0, 1, 2 중 하나여야 한다. 이 중 하나의 값으로 수정하면 정상적으로 컴파일 및 실행되는 것을 확인할 수 있다.
또한 배열은 강제로 런타임에러를 발생시킬 수 있다는 점을 기억하자. 런타임 에러란 컴파일에서는 문제가 없지만 프로그램 실행 시 발생하는 에러를 의미한다.
main.rs
를 다음과 같이 수정한다.datatypes/src/main.rs
use std::io; fn main() { let a = [1, 2, 3, 4, 5]; println!("Please enter an array index."); let mut index = String::new(); io::stdin() .read_line(&mut index) .expect("Failed to read line"); let index: usize = index .trim() .parse() .expect("Index entered was not number"); let elem = a[index]; println!("The value of the elem at index {} is: {}", index, elem); }
위 프로그램은 배열을 초기화한 후 사용자의 숫자 입력을 받는다. 해당 숫자의 인덱스로 배열을 접근하여
elem
을 초기화한 후 출력하는 프로그램이다. 컴파일 및 실행하여, 10을 입력해보자.$ cargo run Compiling datatypes v0.1.0 (/Users/gurumee/Workspace/today-i-learned/getting-started-rust-programming/ch03/datatypes) Finished dev [unoptimized + debuginfo] target(s) in 0.40s Running `target/debug/datatypes` Please enter an array index. 10 # 10입력 후 엔터 thread 'main' panicked at 'index out of bounds: the len is 5 but the index is 10', src/main.rs:17:16 note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
컴파일은 정상적으로 수행되어 프로그램이 시작된다. 그러나 10을 입력 받으면 배열 길이를 초과하여 인덱스를 접근했다고 에러가 발생하며 프로그램이 강제 종료되는 것을 확인할 수 있다. 0 ~ 4 이내에 값을 입력하면 정상적으로 종료되는 것을 확인할 수 있다.
$ cargo run Finished dev [unoptimized + debuginfo] target(s) in 0.00s Running `target/debug/datatypes` Please enter an array index. 3 The value of the elem at index 3 is: 4
728x90'레거시 > Rust' 카테고리의 다른 글
보편적인 프로그래밍 개념 - 변수와 상수 (0) 2022.02.26 cargo 맛보기 (0) 2022.02.20 rust 시작하기 (0) 2022.02.19