bbingju / rust-note

러스트 언어에 관한 개인적인 노트

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Rust Note

개발환경

  • 간단한 코드 테스트는 Rust Playground에서 할 수 있다.
  • 프로그래밍에 필요한 여러 부가정보는 Rust Forge에서 확인할 수 있다.

Toolchains

Command Line Tools

유닉스 계열 운영체제는 다음과 같이 rustup-init.sh 스크립트를 내려받은 후 실행시켜 설치할 수 있다.

curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh

이 방법으로 플랫폼에 맞는 rustup 명령을 설치할 수 있다. rustup 은 일종의 패키지 매니저이며, 이것을 통해 실제 Rust용 툴체인들을 설치할 수 있다.

rustup

설치된 패키지들을 업데이트하려면 다음과 같이 명령한다.

rustup update
  stable-x86_64-unknown-linux-gnu unchanged - rustc 1.65.0 (897e37553 2022-11-02)

로컬로 문서를 보고 싶다면 다음과 같이 명령한다.

rustup doc

Editors

Emacs

Emacs로 Rust 언어를 프로그래밍 하는 기본적인 방법은 rust-mode 을 설치하는 것이다. 필요할 경우, LSP Modetree-sitter를 고민해 볼 수 있다.

Data Types

  • Scalar와 compound로 나뉨.
  • Scalar는 한개짜리 값을 의미하는데, integers, floating-point numbers, Booleans, 문자열 등이 여기에 속함.
  • Integer type에 isize, usize 라는 것이 있음.
  • Integer literal은 다음과 같이 표기함
    Number literalsExample
    Decimal98_222
    Hex0xff
    Octal0o77
    Binary0b1111_0000
    Byte (u8 only)=b’A’=

Tuple

Tuple은 괄호’()‘를 사용한다.

fn main() {
    let empty_tuple = (); // 빈 투플
    let my_tuple = (23, "test tuple", vec![34, 23, 9]);

    println!(
        "empty tuple: {:?}, my_tuple.0: {:}, my_tuple.1: {:?}",
        empty_tuple, my_tuple.0, my_tuple.1
    );
}
empty tuple: (), my_tuple.0: 23, my_tuple.1: "test tuple"

Enums

Enums는 C/C++와 같이 표현된다.

enum IpAddrKind {
    V4,
    V6,
}

그리고 이것을 쓰는 방식은 아래와 같다.

let four = IpAddrKind::V4;
let six = IpAddrKind::V6;

: use 문을 써서 버거로움을 줄일 수 있다.

use IpAddrKind::*;

let four = V4;
let six = V6;

함수의 인자는 다른 변수를 다룰 때와 같이 사용할 수 있다.

fn route(ip_kind: IpAddrKind) {}

여기서 우리가 두 주소체계를 모두 표현하는 하나의 구조체를 만든다고 가정하고 C 스타일로 정의한다면 아마 다음과 같이 할 수 있을 것이다.

enum IpAddrKind {
    V4,
    V6,
}

struct IpAddr {
    kind: IpAddrKind,
    address: String,
}

let home = IpAddr {
    kind: IpAddrKind::V4,
    address: String::from("127.0.0.1"),
};

let loopback = IpAddr {
    kind: IpAddrKind::V4,
    address: String::from("::1"),
};

하지만 Rust에서는 이것을 enum 만으로 더 간편하게 표시할 수 있다.

enum IpAddr {
    V4(String),
    V6(String),
}

let home = IpAddr::V4(String::from("127.0.0.1"));

let loopback = IpAddr::V6(String::from("::1"));

게다가 Enum에서 변수 타입을 개별로 설정할 수 있어서 이런 경우엔 struct를 쓰는 것보다 편하다.

enum IpAddr {
    V4(u8, u8, u8, u8),
    V6(String),
}

let home = IpAddr::V4(127, 0, 0, 1);

let loopback = IpAddr::V6(String::from("::1"));

Structs

구조체를 다음과 같이 정의 한다.

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

사용은 이렇게 한다.

fn main() {
    let user1 = User {
        email: String::from("someone@example.com"),
        username: String::from("someusername123"),
        active: true,
        sign_in_count: 1,
    };
}

구조체 멤버를 field 라고 하는데, 접근은 C 계열과 같이 . 표기법을 이용한다. 필드의 값을 바꾸고 싶다면 구조체를 mut 하게 선언해야 한다. 개별 필드별로 mutable 하게 만들 수는 없음을 주의. 그래서 구조체를 항상 immutable 하게 만들고 싶다면 아래와 같이 builder 함수를 만들어 이용하는 방법이 있다.

fn build_user(email: String, username: String) -> User {
    User {
        email: email,
        username: username,
        active: true,
        sign_in_count: 1,
    }
}

필드 짧게 만들기

위 구조체는 다음과 같이 줄일 수 있다.

fn build_user(email: String, username: String) -> User {
    User {
        email,
        username,
        active: true,
        sign_in_count: 1,
    }
}

Creating Instances From Other Instances With Struct Update Syntax

위 예제에 추가하여 user2 인스턴스를 더 생성하는데 특정 필드만 다르다고 가정해보자. user2 를 생성하려면 일반적으로 다음과 같이 해야할 것이다.

fn main() {
    // -- snip --

    let user2 = User {
        active: user1.active,
        username: user1.username,
        email: String::from("another@example.com"),
        sign_in_count: user1.sign_in_count,
    };
}

user1 인스턴스와는 email 필드만 다른 user2 인스턴스가 생성되었다. 이것을 다음과 같이 단순하게 표현할 수 있다.

fn main() {
    // -- snip --

    let user2 = User {
        email: String::from("another@example.com"),
        ..user1
    };
}

필드가 없는 구조체

필드가 없는 구조체를 정의할 수 있는데, 이것을 unit-like structs 라고 한다. 이것은 () 와 비슷하게 행동한다.

struct AlwaysEqual;

fn main() {
    let subject = AlwaysEqual;
}

메서드

메서드는 impl 문을 이용하여 구조체 맥락 내에서 정의한다.

#[derive(Debug)]
struct Rectangle {
    width: u32,
    height: u32,
}

impl Rectangle {
    fn area(&self) -> u32 {
        self.width * self.height
    }
}

fn main() {
    let rect1 = Rectangle {
        width: 30,
        height: 50,
    };

    println!(
        "The area of the rectangle is {} square pixels.",
        rect1.area()
    );
}
The area of the rectangle is 1500 square pixels.

Generic Types

fn give_thing<T>(input: T) -> T {
    input
}

fn main() {
    let x = give_thing(String::from("Hello"));
    let y = give_thing(345);

    println!("{}", x);
    println!("{}", y);
}
Hello
345

좀 더 실용적인 예를 든다면, 입력 받은 제네릭한 값을 출력하는 경우를 생각해볼 수 있다.

fn compare_and_print(statement: ???, num_1: ???, num_2: ???) {
    println!(
        "{}! Is {} greater than {}? {}", 
        statement, 
        num_1, 
        num_2, 
        num_1 > num_2
    );
}

fn main() {
    compare_and_print("Listen up!", 9, 8);
}
use std::cmp::PartialOrd;
use std::fmt::Display;

fn compare_and_print<T: Display, U: Display + PartialOrd>(statement: T, num_1: U, num_2: U) {
    println!(
	  "{}! Is {} greater than {}? {}",
	  statement,
	  num_1,
	  num_2,
	  num_1 > num_2
    );
}

fn main() {
    compare_and_print("Listen up!", 9, 8);
}
Listen up!! Is 9 greater than 8? true

: compare_and_print 함수의 인자를 다음과 같이 표현할 수 도 있다.

use std::cmp::PartialOrd;
use std::fmt::Display;

fn compare_and_print<T, U>(statement: T, num_1: U, num_2: U) 
where 
      T: Display,
      U: Display + PartialOrd,
{
    println!(
        "{}! Is {} greater than {}? {}",
        statement,
        num_1,
        num_2,
        num_1 > num_2
    );
}

fn main() {
    compare_and_print("Listen up!", 9, 8);
}

HashMap & BTreeMap

BTreeMap 은 정렬가능한 HashMap 이라고 할 수 있다.

HashSet & BTreeSet

BinaryHeap

VecDeque

Ring buffer 이다.

예를 들어, 아이템 개수가 60만개인 벡터에서 아이템을 낱개로 모두 삭제하려면 많은 시간이 걸린다.

use std::time::Instant;

fn main() {
    let mut big_vec = vec![0; 600_000];

    let start = Instant::now();
    for _i in 0..600_000 {
        big_vec.remove(0);
    }
    let duration = start.elapsed();

    println!("Elapsed time: {:?}", duration);
}
The time elapsed is 31.383903273s

이런 경우엔 VecDeque 를 사용하여 시간을 줄일 수 있다.

use std::collections::VecDeque;
use std::time::Instant;

fn main() {
    let mut big_vec = VecDeque::from(vec![0; 600_000]);

    let start = Instant::now();
    for _i in 0..600_000 {
        // big_vec.remove(0);
        big_vec.pop_front();
    }
    let duration = start.elapsed();

    println!("Elapsed time: {:?}", duration);
}
The time elapsed is 5.52107ms

Loops

loop

  • break 문을 이용하여 루프를 빠져나올 수 있다.
  • 루프별로 이름(tagging)을 줄 수 있다.
#![allow(unused_labels)]
fn main() {
    let mut i = 0;
    let mut j = 0;

    'outer: loop {
        i += 1;
        if i > 3 {
            'inner: loop {
                j += 1;
                if j == 3 {
                    break 'outer;
                }
            }
        }
    }
}

while

for

Iterators

Methods

rev

Iterator의 진행방향을 뒤집는다.

fn main() {
    let a = vec![1, 2, 3];
    let mut iter1 = a.iter().rev();
    let mut iter2 = a.iter().rev().rev();

    assert_eq!(iter1.next(), Some(&3));
    assert_eq!(iter1.next(), Some(&2));
    assert_eq!(iter1.next(), Some(&1));

    assert_eq!(iter1.next(), None);

    assert_eq!(iter2.next(), Some(&1));
    assert_eq!(iter2.next(), Some(&2));
    assert_eq!(iter2.next(), Some(&3));

    assert_eq!(iter2.next(), None);

}

any

find

predicate를 만족하는 이터레이터 요소(element)를 찾는다. 다시말해 findtrue 혹은 false 를 반환하는 클로저(closure)를 받아 객체가 가진 이터레이터 요소를 평가하고 해당 클로저가 true인 경우 Some(element) 를 반환하고 그 외에는 None 을 반환한다.

fn find<P>(&mut self, predicate: P) -> Option<Self::Item>
where
    Self: Sized,
    P: FnMut(&self::item) -> bool,
fn main() {
    let a = vec![1, 2, 3];

    let mut iter = a.iter();

    assert_eq!(iter.find(|&&x| x == 2), Some(&2));
    assert_eq!(iter.next(), Some(&3));
}

cycle

fn main() {
    let even_odd = vec!["even", "odd"].into_iter().cycle();

    let even_odd_vec = (0..5).zip(even_odd).collect::<Vec<(i32, &str)>>();

    println!("{even_odd_vec:?}");
}
[(0, "even"), (1, "odd"), (2, "even"), (3, "odd"), (4, "even")]

take & skip

fn main() {
    let ten_chars = ('a'..).take(10).collect::<Vec<_>>();
    println!("{ten_chars:?}");

    let ten_chars_after_skip = ('a'..).skip(300).take(10).collect::<Vec<_>>();
    println!("{ten_chars_after_skip:?}");
}
['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j']
['ƍ', 'Ǝ', 'Ə', 'Ɛ', 'Ƒ', 'ƒ', 'Ɠ', 'Ɣ', 'ƕ', 'Ɩ']

fold

fn main() {
    let numbers = vec![4, 5, 1, 6, 7];

    println!("{}", numbers.iter().sum::<i32>());

    println!(
        "{}",
        numbers
            .iter()
            .fold(0, |total_so_far, next| total_so_far + next)
    );
}
23
23

다른 예제.

fn main() {
    let a_string = "I don't have any dashes.";

    let dashed = a_string
        .chars()
        .fold('_'.to_string(), |mut string_so_far, next_char| {
            string_so_far.push(next_char);
            string_so_far.push('-');
            string_so_far
        });

    println!("{dashed}");
}
_I- -d-o-n-'-t- -h-a-v-e- -a-n-y- -d-a-s-h-e-s-.-

또 다른 예제.

use std::cmp::max;

fn main() {
    let numbers = vec![34, 8234, -123, 13483, 1634, -340123, 4382];

    let biggest = numbers.iter().fold(
        i32::MIN,
        |bigger, &next| {
            if bigger < next {
                next
            } else {
                bigger
            }
        },
    );

    let another = numbers
        .into_iter()
        .fold(i32::MIN, |bigger, next| max(bigger, next));

    println!("{biggest}");
    println!("{another}");
}
13483
13483

Closures

클로저는 lambda 함수와 함께 anonymous functions 로 분류된다. 클로저는 외부 환경(스코프 밖)을 참조할 수 있다는 점이 람다 함수와 다르다. 표기법은 함수 인자 자리에 || 를 대신 쓴다고 보면 된다.

아래 예시는 모두 같은 기능을 한다.

fn add_one_v1  (x: u32) -> u32 { x + 1 }  // function
fn add_one_v2  |x: u32| -> u32 { x + 1 }; // closure
fn add_one_v3  |x| -> { x + 1 };          // closure
fn add_one_v4  |x| ->  x + 1;             // closure

참고로 u32 은 annotation인데 생략할 수 있다. 그럴 경우, 컴파일러가 type inference를 대신한다.

fn main() {
    let number = 30;
    let my_closure = |x: i32| println!("{}", x + number);

    my_closure(3)
}
33

벡터를 사용한 예.

fn main() {
    let my_vec = vec![2, 5, 3];

    let double_vec = my_vec.iter().map(|number| number * 2).collect::<Vec<i32>>();

    println!("{double_vec:?}");
}
[4, 10, 6]

Traits

다른 언어에서 interface라 일컬어지는 것과 비슷한 면이 있다. 참고.

새로운 Trait 만들기

struct Animal {
    name: String,
}

trait Canine {
    // default implementation
    fn bark(&self) {
        println!("Woof Woof");
    }
}

impl Canine for Animal {
    fn bark(&self) {
        println!("My name is {}. 멍멍", self.name);
    }
}

fn main() {
    let my_animal = Animal {
        name: "바둑이".to_string(),
    };

    my_animal.bark();
}
My name is 바둑이. 멍멍

Another Trait

struct Monster {
    health: i32,
}

struct Wizard {}
struct Ranger {}

trait FightClose {
    fn attack_with_sword(&self, opponent: &mut Monster) {
        opponent.health -= 10;
        println!(
            "Striked with your sword! The opponent's health is now {}",
            opponent.health
        );
    }
    fn attack_with_hand(&self, opponent: &mut Monster) {
        opponent.health -= 2;
        println!(
            "Striked with your fist! The opponent's health is now {}",
            opponent.health
        );
    }
}

impl FightClose for Wizard {}
impl FightClose for Ranger {}

trait FightFromDistance {
    fn attack_with_bow(&self, opponent: &mut Monster, distance: i32) {
        if distance < 10 {
            opponent.health -= 10;
            println!(
                "Attacked with a bow! The opponent's health is now {}",
                opponent.health
            );
        }
    }
    fn attack_with_rock(&self, opponent: &mut Monster, distance: i32) {
        if distance < 3 {
            opponent.health -= 4;
            println!(
                "Attacked with a rock! The opponent's health is now {}",
                opponent.health
            );
        }
    }
}

impl FightFromDistance for Ranger {}

fn main() {
    let radagast = Wizard {};
    let aragorn = Ranger {};

    let mut uruk_hai = Monster { health: 40 };

    radagast.attack_with_sword(&mut uruk_hai);
    aragorn.attack_with_bow(&mut uruk_hai, 17);
}
Striked with your sword! The opponent's health is now 30

Traits as bounds

use std::fmt::Debug;

struct Monster {
    health: i32,
}

#[derive(Debug)]
struct Wizard {
    health: i32,
}

#[derive(Debug)]
struct Ranger {
    health: i32,
}

trait Magic {}
trait FightClose {}
trait FightFromDistance {}

impl Magic for Wizard {}
impl FightClose for Ranger {}
impl FightClose for Wizard {}
impl FightFromDistance for Ranger {}

fn attack_with_bow<T>(character: &T, opponent: &mut Monster, distance: u32)
where
    T: FightFromDistance + Debug,
{
    if distance < 10 {
        opponent.health -= 10;
        println!(
            "Attacked with a bow! The opponent's health is now {}. You are now at: {character:?}",
            opponent.health
        );
    }
}

fn attack_with_sword<T>(character: &T, opponent: &mut Monster)
where
    T: FightClose + Debug,
{
    opponent.health -= 10;
    println!(
        "Striked with your sword! The opponent's health is now {}. You are now at: {character:?}",
        opponent.health
    );
}

fn fireball<T>(character: &T, opponent: &mut Monster, distance: u32)
where
    T: Magic + Debug,
{
    if distance < 15 {
        opponent.health -= 20;
        println!("You raise your hands and cast a fireball! Your opponent now has {} health left. You are now at: {character:?}", 
                 opponent.health);
    }
}

fn main() {
    let radagast = Wizard { health: 60 };
    let aragon = Ranger { health: 80 };

    let mut uruk_hai = Monster { health: 40 };

    attack_with_sword(&radagast, &mut uruk_hai);
    attack_with_bow(&aragon, &mut uruk_hai, 7);
    fireball(&radagast, &mut uruk_hai, 14);
}
Striked with your sword! The opponent's health is now 30. You are now at: Wizard { health: 60 }
Attacked with a bow! The opponent's health is now 20. You are now at: Ranger { health: 80 }
You raise your hands and cast a fireball! Your opponent now has 0 health left. You are now at: Wizard { health: 60 }

Blanket Trait Implementations

use std::fmt::Debug;

trait Prints: Debug {
    fn prints_something(&self) {
        println!("I am {:?}.", self);
    }
}

#[derive(Debug)]
struct Person;

impl<T: Debug> Prints for T {}

fn main() {
    let a_person = Person;
    a_person.prints_something();

    // let x = String::from("hello");
    // x.prints_something();
}
I am Person.

혹은,

use std::fmt::Debug;

trait Prints {
    fn prints_something(&self)
    where
        Self: Debug,
    {
        println!("I am {:?}.", self);
    }
}

#[derive(Debug)]
struct Person;

impl<T> Prints for T {}

fn main() {
    let a_person = Person;
    a_person.prints_something();

    // let x = String::from("hello");
    // x.prints_something();
}
I am Person.

Examples

AsRef

Interator

  • iter():: 레퍼런스 만 할 경우 (&T)
  • iter_mut():: 수정할 수 있는 레퍼런스가 필요한 경우 (&mut T)
  • into_iter():: 사용한 것은 없애는 경우

Chaning Methods

벡터에 순차적인 숫자를 넣은 자료구조를 만들려고 할 때, C언어 스타일로 짠다면 아래와 비슷.

fn main() {
    let mut vec = Vec::new();
    let mut count = 1;

    while count < 11 {
        vec.push(count);
        count += 1;
    }

    println!("{vec:?}");
}
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

Rust 스타일은 다음과 같다.

fn main() {
    let vec1 = (1..=10).collect::<Vec<i32>>();
    // or
    let vec2 = (1..=10).collect::<Vec<_>>();

    println!("{vec1:?}");
    println!("{vec2:?}");
}
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

여기서 벡터의 특정아이템에 접근하는 코드가 필요하다면 chaining methods를 이용할 수 있다.

fn main() {
    let vec = (1..=10).collect::<Vec<_>>();

    let new_vec = vec
        .into_iter()
        .skip(2)
        .skip(1)
        .skip(1)
        .take(5)
        .collect::<Vec<_>>();

    println!("{new_vec:?}");
}
[5, 6, 7, 8, 9]

사실들

Immutable variables과 Constants의 차이점

  • 둘 다 변하지 않는 값을 표현하는 방식이지만, Constants는 mut 을 붙여서 mutable 하게 만들 수 없음.
  • Constants는 let 을 쓸 수 없고 const 를 붙이며, 해당 값의 타입은 항상 annotate 해야함.

match

switch 문과 비슷한 기능을 하지만 더 편리하다.

fn main() {
    let sky = "clear";
    let temperature = "warm";

    // 다중 상태를 체크할 수 있다. 이 때는 tuple 사용.
    match (sky, temperature) {
        ("cloudy", "cold") => println!("It's a bad day."),
        ("cloudy", "warm") => println!("It's a blue day."),
        ("cloudy", _) => println!("It's a cloudy day."),
        _ => println!("Not sure what the weather is.")
    }
}
Not sure what the weather is.

if 문을 같이 쓰는 경우:

fn main() {
    let children = 5;
    let married = true;

    match (children, married) {
        (children, married) if !married => println!("Not married with {} children", children),
        (c, m) if c == 0 && m => println!("Married with no child"),
        _ => println!("Some other type of marriage an children combination")
    }
}
Some other type of marriage an children combination

언더바 ’_‘의 쓰임새

‘나머지 것’ 혹은 ‘쓰이지 않음’ 등의 의미로 쓰인다.

match 문에서 사용예

fn main() {

    let a_num = 9;

    match a_num {
        0 => println!("It's a zero"),
        1 => println!("It's a one"),
        _ => println!("It's an unknown number")
    }
}
It's an unknown number

tuple의 item을 각 변수로 맵핑할 때

fn main() {
    let a_tuple = ( "hello", 9292, vec![1, 2, 3]);
    let (a, _, b) = a_tuple;

    println!("a is {:?}", a);
    println!("b is {:?}", b);
}
a is "hello"
b is [1, 2, 3]

골뱅이 ’@‘의 쓰임새

뭔가 구체적으로 더 기술해야할 때 사용될 수 있다. 예를 들어 match 문에서:

fn match_number(input: i32) {
    match input {
        number @ 0..=10 => println!("It's between 0 and 10. It's the number {}", number),
        _ => println!("It's greater than ten"),
    }
}

fn main() {
    match_number(10);
    match_number(23);
}
It's between 0 and 10. It's the number 10
It's greater than ten

물음표 ’?‘의 쓰임새

use std::num::ParseIntError;

fn parse_as_int(input: &str) -> Result<i32, ParseIntError> {
    let parsed_number = input.parse::<i32>()?;
    Ok(parsed_number)
}

fn main() {
    for item in vec!["test", "3", "9342", "34.9"] {
        let parsed = parse_as_int(item);
        println!("{:?}", parsed);
    }
}
Err(ParseIntError { kind: InvalidDigit })
Ok(3)
Ok(9342)
Err(ParseIntError { kind: InvalidDigit })

prelude 는 Rust를 사용하는데 있어 기본적이고 사용빈도가 높은 모듈들을 정의하고 그것들을 use 문을 쓰지 않고 쓸 수 있게 만들어둔 것이다. 즉, 모든 Rust 프로그램에 자동으로 불러지는 것들의 목록이라 할 수 있다.

Option

Rust에는 NULL 이 없다. 단지 nullable 포인터만 있는데 Option 이 그 기능을 한다. Prelude에 포함되어 있다.

and_then

fn main() {
    let some_output = Some(vec![0, 1, 3, 4, 9]);

    let first = some_output
        .clone()
        .map(|some_vec| some_vec.iter().map(|num| num + 1).collect::<Vec<_>>());

    let second = some_output.map(|some_vec| match some_vec.len() {
        0 => None,
        1 => Some(vec![some_vec[0]]),
        _ => Some(some_vec),
    });

    println!("{first:?}");
    println!("{second:?}");
}
Some([1, 2, 4, 5, 10])
Some(Some([0, 1, 3, 4, 9]))
fn main() {
    let some_output = Some(vec![0, 1, 3, 4, 9]);

    // ...

    let second = some_output.and_then(|some_vec| match some_vec.len() {
        0 => None,
        1 => Some(vec![some_vec[0]]),
        _ => Some(some_vec),
    });

    // ...

    println!("{second:?}");
}
Some([1, 2, 4, 5, 10])
Some([0, 1, 3, 4, 9])
fn main() {
    let some_output = Some(vec![0, 1, 3, 4, 9]);

    // ...

    let second = some_output
        .map(|some_vec| match some_vec.len() {
            0 => None,
            1 => Some(vec![some_vec[0]]),
            _ => Some(some_vec),
        })
        .flatten();

    // ...

    println!("{second:?}");
}
Some([1, 2, 4, 5, 10])
Some([0, 1, 3, 4, 9])

and, or

fn main() {

    let x = Some(2);
    let y = None;

    assert_eq!(x.or(y), Some(2));
}

if let & while let

Turbofish

Toilet Closure

|_|. 변기 같이 생겨서 붙여진 이름.

ok_or, ok_or_else

ok
Result to Option
ok_or
Option to Result
ok_or_else
Option to Result with closure

Projects

Crates

라이브러리 이다. Cargo로 라이브러리 프로젝트를 생성하려면 --lib 옵션을 붙인다. 다음과 같이다 한다.

cargo new --lib <new_lib_proj>

Command Line Argument Parser.

See Also

About

러스트 언어에 관한 개인적인 노트