Rust 자료형
- rust에서 기본적으로 제공하는 자료형은 아래와 같다
| 자료형 | 크기 | rust 표기 |
|---|
| 8bit 정수 | 1 byte | i8 |
| 16bit 정수 | 2 byte | i16 |
| 32bit 정수 | 4 byte | i32 |
| 64bit 정수 | 8 byte | i64 |
| 32bit 실수 | 4 byte | f32 |
| 64bit 실수 | 8 byte | f64 |
| 불리언 | 1 byte | bool |
| 문자 | 4 byte | char |
| 튜플 | 가변 | ( ) |
| 배열 | 가변 | [ ] |
- 문자열은
std::string::String 내장 모듈로 제공된다
fn main() {
let number = 30;
let long_number: i64 = 123456789123456789;
println!("32bit 정수: {}", number);
println!("64bit 정수: {}", long_number);
}
overflow
- 자료형의 범위를 초과하는 경우 overflow가 아닌 panic이 발생한다
- 디버그 모드에서는 panic, 릴리즈 모드에서는 overflow를 체크하지 않는다
fn main() {
let mut a: i8 = i8::MAX;
a = a + 1;
println!("a = {}", a);
}
tuple
fn main() {
let tuple = (1, true, 'a');
println!("{}, {}, {}", tuple.0, tuple.1, tuple.2);
}
배열
- 배열에는 한가지 자료형만 선언할 수 있다
- 배열의 크기는 고정이다
fn main() {
let arr = [1, 2, 3, 4];
for num in arr {
println!("{}", num);
}
}
buffer overflow
- rust에서 buffer overflow가 런타임에 발생하게 되면 panic이 발생한다
use std::io;
fn main() {
let mut arr = [1, 2, 3, 4];
let mut read = String::new();
io::stdin().read_line(&mut read).unwrap();
let index:i32 = read.trim().parse().unwrap();
println!("arr[{}] = {}", index, arr[index as usize]);
}
- 이렇게 오류가 발생하였을때
RUST_BACKTRACE 환경변수가 있다면 backtrace를 볼 수 있다
sijin@Sijin:~/hello-world$ RUST_BACKTRACE=1 cargo run
문자열
- 문자열은 str타입과 String 타입이 있다
- str 자료형은 기본 자료형으로 불변이다
fn main() {
let s: &str = "hello world";
println!("{}", s);
let slice = &s[0..5];
println!("{}", slice);
let upper = s.to_uppercase();
println!("{}", upper);
}
- String 자료형은 동적으로 힙 메모리에 저장되는 자료형으로, 문자열 크기를 동적으로 늘리고 줄일 수 있다
fn main() {
let mut s = String::from("Hello");
s.push_str("Rust");
println!("{}", s);
}
불변성
- 러스트의 모든 변수는 기본적으로 불변성의 성질을 갖는다
- 불변성은 변수가 생성된 뒤 값을 변경하지 못한다는 의미이다
- 함수형 언어의 특징 중 하나이다
- 변수가 불변성을 가지게 되면 병렬 처리에서 race condition이 생기는 경우가 줄어든다
- 함수에서 함수 바깥의 변수를 변경하는 side effect가 줄어든다
- 동일한 파라미터에 동일한 값이 return되는 stateless function을 사용하게 된다
섀도잉
fn main() {
let var = 1;
println!("var={}", var);
let var = var + 1;
println!("var={}", var);
}
- var에 변경이 필요할 때, var에
mut 키워드를 붙혀 가변 변수로 정의할 수 있다
- 그러나 가변변수는 최소화하는 것이 좋다
mut 대신 위와같이 var를 새로 생성하고 기존 var는 소멸하도록 동작시킬 수 있다
- 이를 섀도잉이라고 한다
제어문
if
fn main() {
let cond = true;
if cond == true {
println!("True");
} else {
println!("False");
}
}
let-if
fn main() {
let cond = true;
let ret = if cond == true {
String::from("True")
} else {
String::from("False")
};
println!("{}", ret);
}
- 위 처럼 if 표현식을 let 구문에서 사용할 수 있다
match
fn main() {
let var = 1;
match var {
1 => println!("1"),
2 => println!("2"),
_ => println!("else")
}
}
- switch와 비슷하게 match를 사용할 수 있다
let-match
fn main() {
let var = 1;
let ret = match var {
1 => String::from("1"),
2 => String::from("2"),
_ => String::from("0")
};
println!("{}", ret);
}
반복문
loop
fn main() {
loop {
...
break;
}
}
- loop는 while(true)와 같다
- break 전까지 무한 반복하고 break를 만나면 탈출한다
for
fn main() {
fn main() {
let arr = [1, 2, 3, 4];
for a in arr {
print!("{}", a);
}
for a in 0..5 { // 5회 반복
print!("{}", a);
}
}
- for-in 형식으로 반복문을 사용할 수 있다
while
fn main() {
let mut cnt = 0;
while cnt < 5 {
println!("{}", cnt);
cnt += 1;
}
}
함수
fn main() {
println!("{}", add(2, 4));
}
fn add(x: i32, y: i32) -> i32 {
x + y
}
익명함수
fn main() {
let x= 1;
let y = 2;
let sum = {
x + y
};
println!("{}", sum);
}
- let과 { }를 활용해 익명함수를 만들 수도 있다
클로저
- 클로저는 주변 범위의 변수를 캡처해 사용할 수 있는 익명 함수이다
- js의 클로저는 함수가 함수를 return 하는 경우에 만들어졌다
- rust에서는
|| 키워드를 사용해 만들 수 있다고 한다
fn main() {
let mut x = 5;
let mut add = |y: i32| {
x += y;
};
add(10);
println!("{}", x);
}
구조체
struct Student {
id: i32,
name: String,
email: String
}
fn create_student(id: i32, name: String, email: String) -> Student {
Student { id: (id), name: (name), email: (email) }
}
- 구조체는 c/c++와 매우 비슷하다
- 구조체 이름은 CamelCase를 사용한다
derive
#[derive(Debug)]
struct Student {
id: i32,
name: String,
email: String
}
fn create_student(id: i32, name: String, email: String) -> Student {
Student { id: (id), name: (name), email: (email) }
}
fn main() {
let stu: Student = create_student(1, "name".to_string(), "email".to_string());
println!("student={:?}", stu);
// student=Student { id: 1, name: "name", email: "email" }
}
- struct에 derive Debug 어노테이션을 적용하면 구조체를 쉽게 출력할 수 있다
구조체 메서드
struct Student {
id: i32,
name: String,
email: String
}
impl Student {
fn greeting(&self) {
println!("hello my name is {}", self.name);
}
}
fn create_student(id: i32, name: String, email: String) -> Student {
Student { id: (id), name: (name), email: (email) }
}
fn main() {
let stu: Student = create_student(1, "name".to_string(), "email".to_string());
stu.greeting();
}
- 구조체 메서드는 구조체 인스턴스와 함께 사용할 수 있다
- 첫번째 파라미터는 &self 이다
연관함수
struct Student {
id: i32,
name: String,
email: String
}
impl Student {
fn greeting(&self) {
println!("hello my name is {}", self.name);
}
fn from(id: i32, name: String, email: String) -> Student {
Student { id: (id), name: (name), email: (email) }
}
}
fn main() {
let stu: Student = Student::from(1, "name".to_string(), "email".to_string());
stu.greeting();
}
- 연관함수는 static 함수랑 개념이 비슷하다
- 파라미터에 &self가 없이 선언하면 연관함수가 된다
enum
enum LogLevel {
Debug,
Warn,
Error,
Fatal
}
enum Message {
Quit,
List(i32),
Put(String),
Get(i32)
}
impl Message {
fn execute(&self) {
match self {
Message::Quit => println!("Quit"),
Message::List(val) => println!("List: {}", val),
Message::Put(val) => println!("Put: {}", val),
Message::Get(val) => println!("Get: {}", val),
}
}
}
fn main() {
let m = Message::Put(String::from("my message"));
m.execute();
}
- 다른 언어의 enum과 달리, enum의 각 열거자에 서로 다른 구조체를 할당할 수 있다
Option 열거형
fn print_optional(val: Option<String>) {
match val {
Some(val) => println!("{}", val),
None => println!("None")
}
}
fn main() {
let some_string = Some(String::from("Rust"));
let none_string = None;
print_optional(some_string);
print_optional(none_string);
}
- Option 열거형은 값이 있을 수도 있고 없을 수도 있는 경우 사용된다
- Rust에서는 null이 없으므로, 대신 Option 열거형을 사용할 수 있다
- Some은 값이 있는 경우, None은 없는 경우를 의미한다
- Option 열거형은 일반적으로 match를 통해 값을 추출한다
test code
#[test]
fn fibo_test() {
assert_eq!(fibo(6), 8);
assert_eq!(fibo(7), 13);
}
- test 어노테이션을 적용해 테스트에서만 동작하는 함수를 선언할 수 있다