Rust – 시작하기

By | 2022년 10월 1일
Table of Contents

Rust – 시작하기

Hello World

fn main() {
    println!("Hello, world!");
}

변수

변수의 불변성

변수는 디폴트로 불변이다.(immutable)
가변변수로 만들려면 mut 를 붙여야 한다.(mutable)

fn main() {
    let i = 10;
    let j : i32 = 20;
    let mut k = 30;

    println!("k = {}", k);

    k = 40;

    println!("i = {}, j = {}, k = {}", i, j, k);
}

tuple

fn main() {
    let (i, j) = (10, 20);

    println!("i * j = {}", i * j);
}

배열(array)

fn main() {
    let lst = [1, 2, 3];

    println!("size = {}, first = {}", lst.len(), lst[0]);

    for i in lst {
        println!("{}", i);
    }
}

String

fn main() {
    let mut s = String::new();
    s.push_str("hello");
    s.push_str(", world!");
    println!("{}", s);

    let mut s = String::from("hello");
    s.push_str(", world!");
    println!("{}", s);
}

캐스팅

fn main() {
    let decimal = 65.4321_f32;

    // Explicit conversion
    let integer = decimal as u8;
    let character = integer as char;

    println!("{}, {}, {}", decimal, integer, character)
}

제어문

if

fn main() {
    let i = 10;

    if i <= 5 {
        println!("i 는 5 이하이다.");
    } else if i <= 10 {
        println!("i 는 10 이하이다.");
    } else {
        println!("i 는 10 보다 크다.");
    }
}
fn main() {
    let i = 10;
    let j = if i != 5 {
        5
    } else {
        6
    };

    println!("j = {}", j);
}

loop, while, for

fn main() {
    let mut i = 1;

    loop {
        println!("i = {}", i);

        if i >= 10 {
            break;
        } else {
            i = i + 1;
        }
    }
}
fn main() {
    let mut i = 1;

    while i <= 10 {
        println!("i = {}", i);
        i = i + 1;
    }
}
fn main() {
    for i in 1..11 {
        println!("i = {}", i);
    }
}

switch(X), match(O)

rust 에서는 switch 보다 더 강력한 match 를 제공한다.

use std::cmp::Ordering;

fn main() {
    let i = 10;
    let j = 20;

    match i.cmp(&j) {
        Ordering::Less    => println!("작습니다."),
        Ordering::Greater => println!("큽니다."),
        Ordering::Equal   => {
            println!("OK!");
        },
    }
}

함수

기초

fn main() {
    println!("메인 함수");

    sub_function();
}

fn sub_function() {
    println!("서브 함수");
}

파라미터 전달

fn main() {
    sub_function(5, 6);
}

fn sub_function(x: i32, y: i32) {
    println!("{} * {} = {}", x, y, x * y);
}

리턴값

return 키워드를 사용할 수도 있고, 아래처럼 마지막 라인에 표현식을 리턴값으로 쓸 수도 있다.

표현식을 리턴값으로 쓸 때는 세미콜론(;)을 생략해야 한다.

fn main() {
    println!("5 * 6 = {}", sub_function(5, 6));
}

fn sub_function(x: i32, y: i32) -> i32 {
    x * y
}

소유권(Ownership)

rust 의 핵심 기능이 바로 소유권이다.

소유권 규칙

  1. 각각의 값은 해당 값의 오너(owner)라고 불리우는 하나의 변수를 갖는다.
  2. 한번에 딱 하나의 오너만 존재할 수 있다.
  3. 오너가 스코프 밖으로 벗어나는 때, 값은 버려진다(dropped).

딱 하나의 오너(소유권 이전)

소유권이 이전되면 이전 변수는 더이상 사용할 수 없다.

소유권 이전 방식은 변수입력, 함수 파라미터 전달, 함수 리턴값의 방식이 있다.

fn main() {
    let s1 = String::from("hello");
    let s2 = s1;

    println!("{}, world!", s2); // ok
    // println!("{}, world!", s1); // error
}

복사

fn main() {
    let s1 = String::from("hello");
    let s2 = s1.clone();

    println!("{}, world!", s2); // ok
    println!("{}, world!", s1); // ok
}

포인터(X), 참조자(O)

rust 는 관리하기 힘든 포인터를 대신해서, 참조자를 이용한다.

참조자는 소유권 이전이 없다

참조자(&) 를 이용해 소유권 이전없이 값을 전달할 수 있다.

fn main() {
    let s1 = String::from("hello");
    let len = calculate_length(&s1);
    println!("The length of '{}' is {}.", s1, len);
}

fn calculate_length(s: &String) -> usize {
    s.len()
}

가변 참조자(Mutable References)

참조자 역시 불변이 디폴트이다.

변경 가능한 참조자를 이용하려면 &mut 를 붙여야 한다.

fn main() {
    let mut s = String::from("hello");
    change(&mut s);
    println!("{}", s)
}

fn change(some_string: &mut String) {
    some_string.push_str(", world");
}

참조자의 규칙

둘 중 한가지의 참조자만 가질 수 있다.

  1. 가변 참조자 단 한개
  2. 여러 개의 불변 참조자

슬라이스(Slices)

컬렉션의 일부에 대한 참조를 슬라이스라고 한다.

TODO

구조체

기본

struct User {
    username: String,
    email: String,
    sign_in_count: u64,
    active: bool,
}

필드 순서는 달라도 상관없다.

let mut user1 = User {
    email: String::from("someone@example.com"),
    username: String::from("someusername123"),
    active: true,
    sign_in_count: 1,
};
user1.email = String::from("skyer9@gmail.com");
println!("{}", user1.username);

기존 구조체의 값을 이용해 새 구조체를 생성할 수 있다.

let user2 = User {
    username: String::from("anotherusername567"),
    ..user1
};

tuple 구조체

struct Color(i32, i32, i32);
struct Point(i32, i32, i32);

let black = Color(0, 0, 0);
let origin = Point(0, 0, 0);

모듈

main.rs

pub mod file1;

fn main() {
    file1::func("Lee");
}

file1.rs

pub fn func(name: &str) {
    println!("Hello {}", name.to_string());
}

메모리 관리

Box<T>

  1. 컴파일 타임에 크기를 알 수 없는 타입을 저장할 수 있다.
  2. 커다란 데이타의 소유권 이전시 복사가 발생하지 않는다.

generic

기본

fn show<T>(x: T) -> T {
    x
}

fn main() {
    println!("{}", show(10.0));
}
struct Point<T, U> {
    x: T,
    y: U,
}

fn main() {
    let p = Point {
        x: 1, y: 2.0
    };

    println!("{}, {}", p.x, p.y);
}

에러가 발생한다.
트레잇(trait) 에서 에러를 방지하는 방법을 설명한다.

// error
fn multiply<T>(x: T, y: T) -> T {
    x * y
}

트레잇(trait)

구조체와 트레잇(trait)

Java interface 와 유사하다.

trait Animal {
    fn custom_bark(&self);

    fn common_bark(&self) {
        println!("Common Bark");
    }
}

struct Dog {
    pomeranian : String,
    poodle : String,
    dashs_hund : String,
}

impl Animal for Dog {
    fn custom_bark(&self) {
        println!("Pomeranian Bark : {}", self.pomeranian);
        println!("Poodle Bark : {}", self.poodle);
        println!("Dashshund Bark : {}", self.dashs_hund);
    }
}

fn main() {
    let dog = Dog{
        pomeranian : String::from("낑낑"),
        poodle : String::from("멍멍"),
        dashs_hund : String::from("댕댕")
    };

    dog.common_bark();
    dog.custom_bark();
}

generic 과 트레잇(trait)

trait Animal {
    fn custom_bark(&self);

    fn common_bark(&self) {
        println!("Common Bark");
    }
}

struct Dog {
    pomeranian : String,
    poodle : String,
    dashs_hund : String,
}

struct Cat {
    ragdoll: String,
    russian_blue: String,
    manx: String,
}

impl Animal for Dog {
    fn custom_bark(&self) {
        println!("Pomeranian Bark : {}", self.pomeranian);
        println!("Poodle Bark : {}", self.poodle);
        println!("Dashshund Bark : {}", self.dashs_hund);
    }
}

impl Animal for Cat {
    fn custom_bark(&self) {
        println!("Cat Bark");
        println!("Ragdoll Bark : {}", self.ragdoll);
        println!("RussianBlue Bark : {}", self.russian_blue);
        println!("Manx Bark : {}", self.manx);
    }
}

fn animal_bark<T : Animal>(animal : T){
    animal.custom_bark();
}

fn main() {
    let dog = Dog{
        pomeranian : String::from("낑낑"),
        poodle : String::from("멍멍"),
        dashs_hund : String::from("댕댕")
    };

    let cat = Cat{
        ragdoll: String::from("냐옹"),
        russian_blue: String::from("캬악"),
        manx: String::from("꾹꾹")
    };

    animal_bark(dog);
    animal_bark(cat);
}

generic 과 트레잇(trait) 여러개

여러개의 트레잇을 받으려면 아래처럼 + 로 추가할 수 있다.

fn some_function<T: Display + Clone, U: Clone + Debug>(t: T, u: U) -> i32 {
    // ......
}

트레잇을 where 쪽으로 옮길 수 있다.

fn some_function<T, U>(t: T, u: U) -> i32
    where T: Display + Clone,
          U: Clone + Debug
{
    // ......
}

multiply() 오류 수정

use std::ops::Mul;

fn multiply<T: Mul<Output=T>>(x: T, y: T) -> T {
    x * y
}

fn main() {
    println!("{}", multiply(5, 6));
}

구구단

fn main() {
    for i in 2..10 {
        for j in 1..10 {
            println!("{} x {} = {}", i, j, i * j);
        }
    }
}

입력받기

&mut guess

& 는 참조자이다.
참조는 디폴트로 불변이므로 mut 를 붙여야 한다.

use std::io;
use std::io::Write;

fn main() {
    print!("숫자를 입력하세요[1-100]: ");
    io::stdout().flush().unwrap();

    let mut guess = String::new();

    io::stdin()
        .read_line(&mut guess)
        .expect("잘못된 값입니다.");

    println!("입력값: {}", guess);
}

랜덤 숫자 생성

Cargo.toml

[dependencies]

rand = "0.8.5"
extern crate rand;

use std::io;
use std::io::Write;
use std::cmp::Ordering;
use rand::Rng;

fn main() {
    println!("숫자 맞추기!");

    let secret_number = rand::thread_rng().gen_range(1..101);

    print!("생각하는 숫자를 입력하세요[1-100]: ");
    io::stdout().flush().unwrap();

    let mut guess = String::new();

    io::stdin()
        .read_line(&mut guess)
        .expect("잘못된 값입니다.");

    let guess: u32 = guess.trim().parse()
        .expect("숫자가 아닙니다.");

    println!("랜덤값: {}", secret_number);
    println!("입력값: {}", guess);

    match guess.cmp(&secret_number) {
        Ordering::Less    => println!("너무 작습니다."),
        Ordering::Greater => println!("너무 큽니다."),
        Ordering::Equal   => println!("빙고!"),
    }
}

숫자 맞추기

extern crate rand;

use std::io;
use std::io::Write;
use std::cmp::Ordering;
use rand::Rng;

fn main() {
    println!("숫자 맞추기!");

    let secret_number = rand::thread_rng().gen_range(1..101);

    loop {
        print!("생각하는 숫자를 입력하세요[1-100]: ");
        io::stdout().flush().unwrap();

        let mut guess = String::new();

        io::stdin()
            .read_line(&mut guess)
            .expect("잘못된 값입니다.");

        let guess: u32 = guess.trim().parse()
            .expect("숫자가 아닙니다.");

        match guess.cmp(&secret_number) {
            Ordering::Less    => println!("너무 작습니다."),
            Ordering::Greater => println!("너무 큽니다."),
            Ordering::Equal   => {
                println!("빙고!");
                break;
            },
        }
    }
}

답글 남기기