Rust Study #2

bepo·2022년 12월 7일
1

Rust

목록 보기
2/3

두번째 Rust 유튜브로 공부하기

진행방식

실행

https://play.rust-lang.org/
https://replit.com/language/rust

학습자료

https://www.youtube.com/watch?v=W9DO6m8JSSs&list=PLfllocyHVgsSJf1zO6k6o3SX2mbZjAqYE&ab_channel=mithradates
http://doc.rust-lang.org/std

1. Intro

Rust → C, C++ 같이 빠르면서 안전한, 하지만 대신 많이 배워야 쓸 수 있는 언어.
까다로울 수록 Compiler가 뭐하는지 알 수 있고 해서 좀 편하다.
끝!

2. Comments

// 기본 주석
/* / C, C++과 같이 영역 주석
/// Document 파일을 만들때 사용되는 주석
let x = 10;
let x: i16 = 10;
let x/
: i16*/ = 10;
위와 같이도 가능함

let x/: i16/ = 10;
처럼
언더바를 상용하면 일단 Compiler에게 무시하라고 시킬 수 있다.

3. Integers

i8, i16, i32, i64, i128, isize
u8, u16, u32, u64, u128, usize
같은 거를 사용할 수 있다.

아무 설정값 안주면 i32로 될 수도 있음 (정확히는 안써있)
서로 다른 타입이면 연산이 안됨
하지만, let으로 아무것도 안정해주면 좀 알아서 맞춰서 연산하게 되는 것 같음

4. Chars

char, mut
mut → mutable, 바꿀수있는 변수
char는 4 byte 크기를 사용하고 있음, 그래서 32-bit를 쓰는데 엄청 많이 표현함
ASCII는 32-bit까지는 필요없어서 casting해서 적게 쓸 수 있음
변수 a as u16 이런게 가능함. 그래서,

let a: u8
let b: u16
c = a + b as u8
로 할 수 있음

Way casting
ASCII (255개) → 8-bit 만으로 모두 표현이 가능함

let my_num = 'a' as u8; (가능)
let my_num = '№' as u8; (불가능)
아 진짜, 출력하는게 엄청 엄청 편해졌다... 와...
Compiler가 엄청 똑똑하게 동작하는 것 같네

5. Chars 2

char과 string의 차이
char은 4B 이지만, string은 몇 Byte일지 모름
rust에서는 len이라는 것이 Byte를 말하는 것이다.
그래서, "a".len() 이런식으로 사용이 가능함!! 미친;
그런데, 해보니까 길이가 나오는데?
아, 그러니까
"abcde".len() = 5
"abcd오".len() = 7
이렇게 나오는걸 봐서, 영어글자는 1 Byte, 한글은 3 Byte로 표현을 하네
.chars().count() 이런 것도 있음
"Hello there".chars*().count() → ('H', 'e', 'l', ...).count
이걸로 글자수를 체크할 수 있고

6. Float

9u8 이런식으로 쉽게 쓸 수 있음, 9_u8, 9__u8 이런것도 가능
1___
000__000u64 이런것도 가능
float는 f32, f64, 이런식으로 사용가능
거의다 f64를 쓰는 추세로 보임
let my_num = 9.6; // f64
let other_num = 9;
float에 as i32처럼 바꾸면 소수점 다 버림

7. println!

rust의 재미있는 부분은 매크로를 많이 쓰는 것임
println (일반)
println! (매크로 함수)
매크로 → 약간 Superfunction
좀 복잡한 코드가 있으면, 좀 깔끔하게 편하게 쓸 수 있게 해줌
매크로 어떻게 했는지 볼 수 있음
TOOLS → expand macros
println! 안에 {} 여러개 둘 때 차례대로 들어가게 됨

fn give_age() -> i32 {
42
}
이런식을 함수를 정의하면 됨
return 값을 i32 이런식으로 씀
return 써도 되고 안써도 되고

println!("{a}") 이런식으로도 쓸 수 있음!!

8. println! 2

println!("Name: {Name}, City: {City}, Age: {Age}", Name = a, City = b, Age = c);
println!("Name: {1}, City: {0}, Age: {2}", b, a, c);
근데 애초에 println!("Name: {a}, City: {b}, Age: {c}"); 가 엄청 편해보이네

9. semicolon, unit type

let x = 9;
semicolon ;

() --> empty tuplepl

return으로 semicolon을 사용하면 void가 나옴. void는 empty_tuple () 형태
empty tuple은 println!이 불가능함
println!의 {}는 std::fmt::Display를 수행하는 것인데, empty tuple은 이를 지원하지 않음
지원하지 않는 것을 어떻게 출력하느냐..
Display {}
Debug print {:?}
위의 Debug print로 할 수가 있다. (대부분의 경우)
main은 return type을 마음대로 바꿀수가 없음

10. functions, code blocks

fn give_number(one: i32, two: i32) -> i32
이런 식으로 수행할 수 있음
내부에서 복잡한 수행을 한 다음에 변수 하나를 return 하고 싶으면 다음과 같이 수행가능
fn mul(a, b) -> i32 {
let result = {
let c = 12;
a b
};
result
}

11. Mutability, Shadowing

mutability : 바꿀 수 있는지

immutable by defuault -> 바꿀 수 없다
mut으로 바꿀 수 있게 변경 가능
ex)
let a = 10; 이면
a = 9 이런거 들어오면 에러가 발생함
근데 let mut a = 10; 이면 수정 가능
그런데 가능하면 안바꾸는게 좀 안전하다

Shadowing : 같은 이름을 다시 쓰는 것
let num = 1;
let num = 2;
이런 식으로 덮어씌울 수 있음
컴파일러가 봤을때에는 문제가 없음
let x = double(x); 이런식으로도 수행가능

let a = 1;
{
let a = 2;
}
println!("{a}");
를 해보면, 1 값이 됨

12. Memory + References

Stack : 제일 빠르고, 1 - 2 - 3 이면 3 - 2 - 1로 되는
접근할 곳이 계속 바로 옆에 있어서 매우매우 빠름
Heap : 컨테이너 처럼 1 - 2 - 3 이면 1 - 2 - 3으로 나오는
근데 보통 list 처럼 수행해서 특정 위치의 값을 읽어오는 식으로 하고 있음
마치 책과 같음. 챕터1: 12pg, 챕터2: 25pg, 챕터3: 81pg ..
&&&&&a 처럼 reference 여러개 써서 할 수 있음

13. Fancy printing

\n으로 새로운 line으로 가는거고 이건 c, c++하고 동일
\n 이런시으로 계속 쓰는 거는 좀 더러우니까
print!(r#"c:\thisdrive\new_drive"#);
이런식으로 깔끔하게 raw text만 출력할 수 있음
더나아가서,
println!("Let me
Tell you
This thing")
이런식으로도 다음 줄로 넘어갈 수 있음

println!("{:?}", )
let num = 9;
println!("{:p}", num) // pointer 출력
{:X} // Hexadecimal 출력
{:b} // binary로 출력
자세한건 Rust의 Module::std::fmt에서 확인할 수 있음
< 왼쪽, > 오른쪽, ^ 가운데
{:-^30} // 가운데 30개
{: <15} // 왼쪽에 15개
외울 필요는 없고 Module::std::fmt에서 할 수 있게

14. String and &str

rust는 여러가지 string 타입을 제공, 총 8가지정도
거의 항상 String, &str를 사용
String: growable string, 더 많은 기능을 사용할 수 있음, owned type이 있음
owned type은 자기 타입을 가지고 있고, 그 데이터가 없어지면 그것도 없어지는, 나중에 좀 편리하다고 함
왜냐하면, &str같은걸 쓰면 얘가 살아있는지 죽은지 확인이 어렵기 때문에
&str > String으로 전환하는 것은 "David".to_string() 이런식으로 가능
혹은 String::from("David");

let mut tmp = "David".to_string();
tmp.push('!'); // 근데 char만 받는다는데??
이런식으로 수행 가능

String은 사실 Sized type (Heap에 저장)
str은 dynamic type (Stack에 저장)

15. String methods

String
.capacity
.push
.push_str
.pop
with_capacity (. 없는게 맞음)
.capacity는 Byte크기를 나타내고.
String은 편한데 allocation과 reallocation하는 과정이 있음
let mut tmp = String::with_capacity(26); 이런식으로 reallocation 없도록 allocate를 할 수 있음

16. Const and Static

let x = 8; // 'let' binding
const는 무조건 어떤 타입인지 써야함, 그리고 이름이 다 대문자가 좋음
const NUMBER: i32 = 20;
static NUMBER: i32 = 0
const를 주로 제일 많이 쓰는데, static은 같은 메모리 공간을 쓰는 보장이 있음
static mut로 할 수 있긴 한데 unsafe함

lifetime이라는 것이 있는데,
static lifetime이라는 것도 있다. 이거는 프로그램 시작 ~ 끝부터 살 수 있는 것

17. Returning a reference (소유권 개념!)

소유권, Ownership
함수내의 이미 사라진 변수에 대해서 return을 하면 이를 compiler가 막음

18. Mutable reference

mutable / unique reference (위험함) (내것을 수정할 수 있음)
imutable / shared reference (내것을 수정할 수 없음)
그러니까, 함수를 쓰는게 남이 하는 거라고생각했을 때, 내거가 안전한지 안한전한지

19. References and Shadowing

mutable로 선언한 변수를 reference하려면 mutable로 받아야 한다.
let a = 10;
let a_ref = &a;
let a = 8;
a_ref -> 10, a -> 8

20. References in functions

함수에게 입력값으로 a를 넘겨줬을 시에, a의 소유권을 함수에게 넘겨준거임
그래서, a를 다시 못씀 (아얘 사라져버림)
그래서, input을 reference로 넘겨주는것으로 반복사용가능
let a = 10;
print_a(a);
print_a(a);
안됨!
let a = 10;
print_a(&a);
print_a(&a);
됨 :)

21. Mutable references and mut in functions

input 줄 때
a // value
&a // reference
&mut a // mutable reference

mutable이 약간 좀 많이 짜보면서 느낌을 알아야겠네,
fn add_is_great(mut tmp: String)
fn add_is_great(tmp: &mut String)

22. Copy and Clone

Copy 타입을 가진 input의 경우, reference를 추가하는 등의 고려가 필요없다.
"It's trivial to copy the bytes"
Copy 타입이 아닌 input은 함수에게 소유권이 이동해서 다시 사용할 수 없음.
그런데, 이 경우에 .clone()을 통해서 해결할 수가 있음.

	let b = "hi".to_string();
    print_string(b.clone());
    print_string(b);

다만, 메모리 사용량이 더 늘어나겠지. 그래서, reference를 익히는게 더 나은것 같아

23. Uninitialized variables and For loops

rust는 let a; 이런게 안됨 (type 특정이 안되기 때문에)
그리고, rust는 uninitialzed variable을 사용할 수 없음 (당연)

let a = {
	let x = 4;
    x + 5
};

이런식으로 쓰는게 좀 보통인가봄

loop {
counter += 1;
if counter % 50 == 0 {
break;
}
}
counter

24. Collection Types

let array = [];
let array = ["One", "Two"]; // [&str; 2]
let array = ["One", "Two", "Five"]; // [&str; 3]

꿀팁 : .method를 이상한 걸 막 쓰면 compiler가 화내면서 type 정보 알려줌
let array = [0; 640]; // 0이 640개
let mut array = [0; 640]; // 변경이 가능한 array
array[0], array[1] 이런식으로 접근 가능하고
array.get(3) 로 좀 더 안전하게 수행할 수 있음.
왜냐하면, 3번이 없을 때 array[3]은 error가 나오는데, get(3)쓰면 none이 나옴

25. Slices

array를 slice하는 것
array[0..2] // 0 이상, 2 미만
array[0..=2] // 0 이상 2 이하
array[..] // 전체
array[3..] // 3 이상
array[..=4] // 4 이하
약간, Array, &str는 간단하고 빠르고
Vec, String은 기능많은 느낌
http://doc.rust-lang.org/std 에서 다양한 standard의 구현을 볼 수 있는데,
String을 보면 [src] 누르고, 사실 Vec[u8]으로 이루어진 것이였음

26. Vecs

앞으로의 [는 꺽새 [일 수도 있음 (velog에서 잘 안됨)]]
Vec[String]
String::new()

let mut tmp = Vec::new()
tmp.push(name1);
tmp.push(name2);
tmp.capacity();
약간, String에서 사용할 수 있는 종류랑 비슷함

let my_vec = Vec::from([8, 9, 10]);

근데, Vec보다 더 편리한게 vec!임, 사람들이 더 좋아함
let my_vec = vec![name1, name2];

27. From and Into

trait = 초능력
From이 있으면, Into를 쓸 수 있음
let name = String::from("Dave MacLeod");
let city: String = "Seoul".into();
영상 보고는 잘 이해가 안되긴 하네..
위에 String으로 변환하는 동작을 into()로 대신하는 것 같음

28. Tuples and Destructuring

() 로 만들 수 있다.
let my_tuple = (8, "Dave MacLeod", vec![8.9.10]);
이런식으로 안에 아무 타입을 넣어도 됨
tuple은 tuple.0, tuple.1, tuple.2 이런식으로 쓰는 것임
Vec[(String, i32)] 이런식으로, Vec안에 tuple을 넣을 수 있음
let my_vec = vec![("Hey", 9), ("Hello there", 91231)] 이런식으로

let tuple = ("one", "two", "three");
let (a, b, c) = tuple;
하면 알아서 3개로 나뉨
이거는 array도 가능함
기억해 array는 [], tuple은 ()

29. Control flow and Match!

if tmp == 7 {

}
뭐 이런식, 괄호가 굳이 필요없음
if / else if / else

&& = and, || = or

match : switch랑 비슷한데 더 좋음
match tmp {
0 => println!("zero!"),
1 => println!("one!"),
_ => println!("else")
}
마지막 예외처리를 해줘야함 ㅇㅇ

let second = match tmp {
0 => 23,
1 => 45,
_ => 67
};

이렇게도 할 수 있고,

30. Match statements

let sky = "cloudy";
let temperature = "warm";
match (sky, temperature) {
("cloudy", "cold") => println!("not that good"),
("clear", "warm") => println!("nice"),
("cloudy", ) => println!("it's cloudy"),
=> println!("I don't know")
}
이거는 위에서 아래로 차례대로 조건문을 보는것이기 때문에, 위에서 이미 선택되면 아래는 보지도 않음

match (children, married) {
(c, m) if m == false => println!("not married"),
(c, m) if c == 0 && m => println!("married"),
}

31. More match statements

match rbg {
(r, , ) if r < 10 =>
(, b, ) if b < 10 =>
(, , g) if g < 10 =>
_ =>
}

그리고, 반환값이 같은 type 이여야함. 어떤거는 integer, 어떤거는 string이면 error나옴
match input {
number @ 0..=10 => println!("hihi, {}", number),
_ => println!("nono")
}
이런것처럼 사용할 수 있음

32. Structs

struct → 자기만의 타입을 만들고 싶을때
비슷한건 enum

struct FileDirectory;
fn takes_file_directory(input: FileDirectory) {
println!(~);
}

struct Color(u8, u8, u8);
let my_col = color(20, 50, 100);
접근은 my_col.1, my_col.2, my_col.3 같은거로 해야함

std::~::Display가 없어서, {:?}로 출력해야함
debug print가 유용한 것 같음 (개발자한테)
{:#?}가 있음 → {:?}보다 조금 더 깔끔하게 print

꿀팁! #[derive(Debug)] : 간단하게 이런 기능을 주고 싶다?

// named struct
struct Country {
population: u32,
capital: String,
president: String,
}

let canada = Country {
population: 35_000_000,
capital: "Ottawa".to_string(),
president: "Justin Trudeau".to_string(),
}

접근은 canada.population, canada.capital, canada.president 처럼 할 수 있음

33. Struct size

let canada = Country {
population: 35_000_000,
capital: "Ottawa".to_string(),
president: "Justin Trudeau".to_string(),
}

꿀팁! Clippy: 에러 검출 + 코드 잘썼는지 확인해줌
굉장히 툴이 많네, 개좋다!

use std::mem::size_of_val;
println!("{}", size_of_val(&canada));

struct Numbers {
one: u8,
two: u8,
three: u8
four: u32
}
size_of_val(Numbers) = 8이 나옴, 이런식으로 Align 때문에 빈공간을 생성하는데,
Clippy가 이런거를 볼 수 있게 해준다는 건가?

34. Enums

// struct = and 개념
// enum = or 개념, 약간 switch/case 문 같은

enum ThingsInTheSky {
Sun, // 0
Stars // 1
Moon // 2
Plane // 3
}

fn createskystate(time: i32) -> ThingsInTheSky {
match time {
6..=18 => ThingsInTheSky::Sun,
=> ThingsInTheSky::Stars
}
}

fn checkskystate(state: &ThingsInTheSky) {
match state {
ThingsInTheSky::Sun => println!("I can see the sun"),
ThingsInTheSky::Stars => println!("I can see the stars")
=> println!("Nothing in the sky")
}
}

35. Enums 2

enum ThingsInTheSky {
Sun, // 0
Stars // 1
Moon // 2
Plane // 3
}

use ThingsInTheSky::* 를 쓰게 되면,
ThingsInTheSky::Sun 이렇게 쓸 필요없이,
Sun 이렇게 쓸 수 있음!

use ThingsInTheSky::*
let four_things = vec![Sun, Stars, Moon, Plane];
이런식으로도 가능

36. Enums 3

enum ThingsInTheSky {
Sun = 0,
Stars = 13,
Moon = 20,
Plane = 43
}
이런식으로 숫자 바꿀 수 있음

숫자 같은 경우에
ThingsInTheSky::Sun as u32 이런식으로 쓰면 바로 숫자로 쓸 수 있음

꿀팁! .is_positive()라는 Method가 있음

enum은 이름과 그에 따른 값을 같이 넣어줄 수 있음
enum Number {
U32(u32)
I32(i32)
}

37. Loops

loop {

}
위에는 While(true) 처럼 계속 반복
break로 중간 해제 가능

꿀팁 : 'first_loop: loop 이런식으로 이름을 붙일 수 있음. 이를 tick이라고 함
근데, 이걸로 한번에 break 할 수 있음!!
'first_loop: loop {
'second_loop: loop {
println!("이런식으로!");
break 'first_loop;
}
}

38. More loops

for num in 0..=3 {

}
처럼 python같이 for문 돌릴 수 있음 끝!

loop를 통해서 어떤 값을 설정할 수도 있음
break counter로 loop의 값 설정가능
let num = loop {
counter += 1;
if counter == 3 {
break counter;
}
}

39. Impl blocks

struct, enum 같은 거는 .len()과 같은 게 없을 수 있는데
이를 구현하는 방법

. #[derive(Debug)] // debug print 선언
struct Animal {
age: u8,
animal_type: AnimalType
}

. #[derive(Debug)]
enum AnimalType {
Cat,
Dog
}

impl Animal {
fn new(age: u8) -> Self { // 굳이 이름이 new가 아니여도 됨, Self나 Animal이나 같음
Self {
age,
animal_type: AnimalType::Cat
}
}
}

fn main() {
let my_animal = Animal::new(10);
}

40. More impl blocks

아래 안에다가 구현할 함수들을 엄청 많이 정의 가능
impl Animal {
fn new_cat() -> Self {

}
fn print(&self) {
	println!("I am a: {:?}", self);
}

}

my_animal.print(); 혹은
Animal::print(&my_animal); // 사실 위에꺼를 Compiler는 이것으로 사용함

41. enum impl blocks

enum도 input으로 활용이 가능하다~
enum(String) 처럼 쓰는 활용도 가능하다.

42. More destructuring

let John = Person {
name: "John",
height: 178,
happiness: flase,
}

// destructuring
let Person {
name: a,
height: b,
happiness: c
} = John;

이런식으로 destructuring을 수행할 수 있음

let Person {
name,
height,
..
} = John;
이런식도 가능 (..)은 여러개 무시

43. Dereferencing and the dot operator

아하, 그 &, 에 대한거임
a = 10
(&a) = 10

44. Generics

반대 : concrete
Generics : 일반적인
generics → T로 표현

fn give_thing<T.>(input: T) -> T {
input
}
// 굳이 T가 아니여도 되긴 함, 막 GenericType 이런 단어여도 됨

use std::fmt::Display;
fn give_thing<T: Display>(input: T) -> T {
input
}

45. More Generics

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

fn compare_and_print<T: Display, U: Display + PartialOrd>(statement T, num_1: U, num_2: U) {
println!(~~~)
}

fn compare_and_print<T, U>(statement T, num_1: U, num_2: U)
where
T: Display,
U: Display + PartialOrd,
{
println!(~~~)
}

46. Option

profile
Dugu Dibap

0개의 댓글

관련 채용 정보