2025년 5월 4일 일요일

8-1강: 리눅스 시스템 프로그래밍 (설정)

 

Windows에서 Rust 리눅스 개발 환경 구축하기 (WSL2 + Ubuntu + VSCode)

리눅스 시스템 프로그래밍이나 FFI(C 연동) 등을 Rust로 개발하려면 리눅스 환경이 필요
Windows 사용자가 WSL2 + Ubuntu + VSCode를 통해 Rust 개발 환경을 구축하는 과정을 정리

✅ 1단계: WSL2 활성화

Windows에 리눅스를 실행할 수 있도록 **WSL2 (Windows Subsystem for Linux 2)**를 설치

💻 PowerShell 관리자 권한으로 실행 후 입력:

wsl --install

⚠️ 설치 후 자동으로 Ubuntu가 설치되지 않았다면 다음 명령어로 수동 설치:
wsl --install -d Ubuntu

설치 후 재부팅이 필요
-----------------------------------------------------------------------------------------------------------------------

✅ 2단계: Ubuntu 초기 설정

재부팅 후 Windows 검색창에서 Ubuntu 실행

→ 최초 실행 시 몇 분 소요됨 

→ 사용자 이름 / 비밀번호 설정

---------------------------------------------------------------------------------------------------------------------

✅ 3단계: Rust 설치 (Ubuntu 내부)

Ubuntu 터미널에서 아래 명령어 입력:

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


→ 안내에 따라 설치 후, 환경 적용:
source $HOME/.cargo/env

→ 설치 확인:
rustc --version
cargo --version


---------------------------------------------------------------------------------------------

✅ 4단계: VSCode 설치 및 확장 설치

VSCode 실행 후 확장(Extensions) 설치:

    - Remote - WSL
    - Rust Analyzer
    - C/C++ 확장 설치
--------------------------------------------------------------------------------------------

✅ 5단계: VSCode로 Ubuntu 연동

1. VSCode 좌측 하단에 >< 아이콘 클릭 -> WSL: Ubuntu 선택

2. 자동으로 우분투 안에서 vscode 가 열림
---------------------------------------------------------------------------------------------

✅ 6단계: 개발 환경 확인

WSL 내부에서 다음을 입력해 확인:

rustc --version

gcc --version # C 연동 시 필요


필요시 build-essential 설치:

sudo apt update

sudo apt install build-essential

---------------------------------------------------------------------------------------------

✅ (선택) bindgen 사용 시 필수

C 헤더 바인딩을 자동 생성하는 bindgen을 사용하려면:

sudo apt install libclang-dev clang

2025년 5월 1일 목요일

7-1-3강 객체 지향(다형성)

러스트는 트레이 객체를 사용해 다형성을 제공한다.

하나의 함수가 다양한 타입을 받아 공통 동작을 할 수 있게 해준다.

// trait 정의
trait Hello {
    fn hello_msg(&self) -> String;
}

// 구조체 정의
struct Student {
    name: String,
}

//trait은 "impl 트레잇 이름 for 구조체 이름" 형식으로 정의 한다.
// Hello 트레잇을 Student 구조체에 구현
impl Hello for Student {
    fn hello_msg(&self) -> String {
        String::from("안녕하세요! 선생님,")
    }
}

// 구조체 정의
struct Teacher {
    name: String,
}

// Hello 트레잇을 Teacher 구조체에 구현
impl Hello for Teacher {
    fn hello_msg(&self) -> String {
        String::from("안녕하세요. 오늘 수업은...")
    }
}

// 다형성을 사용하기 위해서는 trait 객체를 사용해야 한다.
// trait 객체는 "&dyn 트레잇 이름" 형식으로 정의한다.
fn say_hello(say: &dyn Hello) {
    println!("{}", say.hello_msg());
}

fn main() {
    let student = Student { name: String::from("luna") };
    let teacher = Teacher { name: String::from("me") };

    say_hello(&student); // Student 구조체의 hello_msg 메서드 호출
    say_hello(&teacher); // Teacher 구조체의 hello_msg 메서드 호출
}

/*실행결과
안녕하세요! 선생님,
안녕하세요. 오늘 수업은...
*/

7-1-2강 객체 지향(상속)

러스트는 클래스 상속을 지원하지 않는다.

구성과 트레잇 구현으로 상속처럼 동작하게 만든다.

// Pointable이라는 트레잇(인터페이스)을 정의합니다.
// 이 트레잇을 구현하는 구조체는 x()와 y() 메서드를 가져야 합니다.
trait Pointable {
    fn x(&self) -> i32;
    fn y(&self) -> i32;
}

// Point 구조체는 x, y 좌표를 가지고 있습니다.
struct Point {
    x: i32,
    y: i32,
}

// Point 구조체에 대해 Pointable 트레잇을 구현합니다.
impl Pointable for Point {
    fn x(&self) -> i32 {
        self.x
    }

    fn y(&self) -> i32 {
        self.y
    }
}

// ColorPoint 구조체는 색상(color)과 Point를 포함하는 구조체입니다.
struct ColorPoint {
    color: String,
    point: Point,
}

// ColorPoint에 메서드를 추가합니다.
impl ColorPoint {    
    // 생성자 메서드: 새로운 ColorPoint 인스턴스를 만듭니다.
    fn new(color: String, x: i32, y: i32) -> ColorPoint {
        ColorPoint {
            color: color,
            point: Point { x: x, y: y },
        }
    }

    // 색상을 반환하는 메서드
    fn color(&self) -> &String {
        &self.color
    }
}

// ColorPoint도 Pointable 트레잇을 구현합니다.
// 내부에 있는 Point의 x, y 값을 위임(delegate)하는 방식입니다.
impl Pointable for ColorPoint {
    fn x(&self) -> i32 {
        self.point.x
    }

    fn y(&self) -> i32 {
        self.point.y
    }
}

// Pointable 트레잇을 참조하는 함수.
// 모든 Pointable 타입을 받아서 좌표를 출력합니다.
fn print_pointable(pointable: &dyn Pointable) {
    println!("x: {} y: {}", pointable.x(), pointable.y());
}

fn main() {
    // ColorPoint 인스턴스를 생성합니다.
    let pt = ColorPoint::new(String::from("red"), 1, 2);
   
    // print_pointable 함수에 전달하면 Pointable로 동작합니다.
    print_pointable(&pt);
}

/*실행결과
x: 1 y: 2 */

7-1-1강 객체 지향(캡슐화)

- 러스트는 기본적으로 객체지향을 언어는 아니다. 고유한 방식으로 구현한다.

1. 캡슐화

구조체의 필드와 메서드에 대한 접근 제어를 통해 캡슐화를 구현한다.

기본적인 모든 필드는 private 다.

pub 키워드를 사용하여 외부에 공개할 수 있다. 

// 트레잇과 같은 형태로 객체지향에 가까운 프로그램을 작성할 수 있다.
pub struct Student {
    id: i32, // private 필드
    pub name: String, // public 필드
    pub email: String, // public 필드
}

impl Student {
    // public 생성자
    pub fn new(id: i32, name: String, email: String) -> Student {
        Student { id, name, email }
    }

    // public 메서드
    pub fn get_name(&self) -> &String {
        &self.name
    }

    // private 메서드
    fn set_name(& mut self, name: String) {
        self.name = name.clone();
    }
}

fn main() {
    let student = Student::new(1, String::from("luna"), String::from("luna@email.me"));
    println!("이름: {}", student.get_name());
}

/*실행결과
이름: luna
*/

2025년 4월 26일 토요일

6-3강: 러스트 라이브러리(Fs, Path, PathBuf)

1. fs : 파일 시스템 관련 작업을 지원하는 표준 라이브러리

//fs 모듈을 사용하여 파일을 생성하고 읽는 예제
use std::fs::File;
use std::io::{self, Read, Write};

//? 전제 조건
// main() 함수나 해당 위치 함수의 반환 타입이 Result<...> 이어야 한다.
// 그래야 에러를 return할 수 있다.
fn main() -> io::Result<()> {
    //?를 붙이면 에러가 발생하면 즉시 현재 함수를 빠져나가고,
    //에러를 호출자에게 전달
    let mut file = File::create("example.txt")?; // 파일 생성

    // b"Hello"처럼 b를 붙이면 &[u8] 타입, 즉 바이트 슬라이스가 된다.
    // write_all 메서드는 &[u8] 타입의 데이터를 받아들이기 때문
    file.write_all(b"Hello, Rust!")?; // 파일에 내용 추가

    // 파일 읽기
    let mut file = File::open("example.txt")?;
    let mut content = String::new();
    file.read_to_string(&mut content)?;
    println!("{}", content);

    Ok(())
}

/*실행결과
Hello, Rust!
*/

2. 디렉터리 생성, 읽기, 삭제

use std::fs;
use std::io;

fn main() -> io::Result<()> {
    // 디렉터리 생성
    fs::create_dir("example_directory")?;
    println!("example_directory 생성됨");

    // 현재 실행 디렉터리의 모든 내용 출력
    let entries = fs::read_dir(".")?;
    println!("현재 실행 디렉터리 내용:");
    for entry in entries {
        let entry = entry?;
        println!("{:?}", entry.path());
    }

    // 디렉터리 삭제
    //fs::remove_dir("example_directory")?;
    //println!("example_directory 삭제됨");

    Ok(())
}
/*실행 결과
example_directory 생성됨
현재 실행 디렉터리 내용:
".\\example_directory"
".\\main.exe"
".\\main.pdb"
".\\main.rs"
*/

3. Path, PathBuf

use std::path::{Path, PathBuf};

fn main() {
    // Path : 참조용, 읽기만 가능
    let path = Path::new("/tmp/test.txt");

    // 경로의 파일명 추출
    if let Some(filename) = path.file_name() {
        println!("파일명: {:?}", filename);
    }

    // 경로의 확장자 추출
    if let Some(extension) = path.extension() {
        println!("확장자: {:?}", extension);
    }

    // PathBuf : 소유 및 수정 가능
    let mut path_buf = PathBuf::from("/tmp/foo");
   
    // 경로에 파일명 추가
    path_buf.push("example.txt");
    println!("전체 경로: {:?}", path_buf);  
}

/*실행 결과
파일명: "test.txt"
확장자: "txt"
전체 경로: "/tmp/foo\\example.txt"
*/

6-2강: 러스트 라이브러리(From, Into, AsRef, AsMut)

1. From : 한 자료형을 다른 자료형으로 변환하는 로직을 정의

2. Into : From 의 역방향으로 자료형을 변환

// 러스트는 타입에 관한 엄격한 안정성을 보장한다.
// 그래서 타입을 변환할 때는 명시적으로 변환을 해줘야 한다.
// From 은 한 자료형을 다른 자료형으로 변환하는 로직을 정의할 때 사용
// Into는 From을 통해 변환된 자료형을 사용.
#[derive(Debug)]
struct Point {
    x: i32,
    y: i32,
}

// Point 구조체를 (i32, i32) 튜플로 변환하는 From trait 구현
impl From<(i32, i32)> for Point {
    fn from(tuple: (i32, i32)) -> Self {
        Point { x: tuple.0, y: tuple.1 }
    }
}

fn main() {
    let tuple = (1, 2);

    // 주어진 tuple을 바탕으로 Point객체를 생성
    let pt: Point = Point::from(tuple);
    println!("Point::from = {:?}", pt);
   
    // tuple을 기반으로 point를 생성합니다. 이때 Point::from이 호출
    let pt: Point = tuple.into();
    println!("tuple.into = {:?}", pt);
}

/*실행결과
Point::from = Point { x: 1, y: 2 }
tuple.into = Point { x: 1, y: 2 }
*/

3. AsRef : 객체를 참조값으로 변환 

// AsRef 트레잇은 어떤 타입을 다른 타입으로 변환할 수 있는 방법
// 이 트레잇을 구현하면, 해당 타입을 다른 타입으로 쉽게 변환
// 예를 들어, String을 &str로 변환하거나, Vec<T>를 &[T]로 변환
// 이 메서드는 AsRef 트레잇을 구현한 타입의 인스턴스에서 호출
struct Person {
    name: String,
    age: u32,
}

impl AsRef<str> for Person {
    // Person의 name을 str형태로 참조할 수 있습니다.
    fn as_ref(&self) -> &str {
        &self.name
    }
}

fn greet_person<P: AsRef<str>>(person: P) {
    println!("안녕! {}!", person.as_ref());
}

fn main() {
    let person = Person { name: String::from("루나"), age: 30 };

    // Person 구조체에 AsRef<str>를 구현했기 때문에,
    // greet_person 함수는 Person을 인자로 받아 사용할 수 있습니다.
    greet_person(person); //안녕! 루나!

    // 물론, String과 &str도 여전히 함수의 인자로 사용할 수 있습니다.
    let name_string = String::from("러스트");
    greet_person(name_string); //안녕! 러스트!
    greet_person("하이!"); //안녕! 하이!!
}

/*실행결과
안녕! 루나!
안녕! 러스트!
안녕! 하이!!
*/

4. AsMut : 객체를 수정 가능한 참조로 변환

// AsMut 는 객체를 수정 가능할 참조로 바꾸는 트레잇이다.
// AsMut 트레잇을 구현한 타입은 &mut T로 변환할 수 있다.
struct Person {
    name: String,
    age: u32,
}

// AsMut 트레잇을 구현하여 name 필드에 대한 가변 참조를 제공한다.
// AsMut 트레잇을 구현하면, &mut T로 변환할 수 있다.
impl AsMut<String> for Person {
    fn as_mut(&mut self) -> &mut String {
        &mut self.name
    }
}

// name_change 함수는 AsMut 트레잇을 구현한 타입에 대해
// name 필드의 값을 변경하는 기능을 제공한다.
// 이 함수는 person 매개변수로 전달된 객체의 name 필드를
// 가변 참조로 가져와서 새로운 이름으로 변경한다.
fn name_change<P: AsMut<String>>(person: &mut P, new_name: &str) {
    let name = person.as_mut();
    name.clear();
    name.push_str(new_name);
}

fn main() {
    let mut person = Person {
        name: String::from("루나"),
        age: 10
    };

    println!("변경 전: {}", person.name); //변경 전: 루나
    name_change(&mut person, "러스트");
    println!("변경 후: {}", person.name); //변경 후: 러스트
}

/*실행 결과
변경 전: 루나
변경 후: 러스트
*/

6-1강: 러스트 라이브러리(Copy, Clone, Drop)

1. Copy 예제

#[derive(Debug, Clone, Copy)]
struct Point {
    x: i32,
    y: i32,
}

fn add_points(p1: Point, p2: Point) -> Point {
    Point {
        x: p1.x + p2.x,
        y: p1.y + p2.y,
    }
}

fn main() {
    let a = Point { x: 1, y: 2 };
    let b = Point { x: 3, y: 4 };

    // add_point의 인자로 들어가는 a, b는 copy트레잇에 의해 복제됩니다.
    let result = add_points(a, b);

    println!("{:?}", a); // a에 접근 가능
    println!("{:?}", b); // b에 접근 가능
    println!("{:?}", result);
}
/*실행결과
Point { x: 1, y: 2 }
Point { x: 3, y: 4 }
Point { x: 4, y: 6 }
*/

2. Clone 예제
// Clone trait을 구현하지 않은 타입은 clone() 메서드를 사용할 수 없다.
#[derive(Debug)]
struct Person {
    name: String,
    age: u32,
    cloned: bool,
}

impl Clone for Person {
    fn clone(&self) -> Self {
        Person {
            name: self.name.clone(),
            age: self.age,
            cloned: true
        }
    }
}

fn main() {
    let person1 = Person {
        name: String::from("루나"),
        age: 10,
        cloned: false
    };

    // person1을 복제합니다. 소유권을 잃지 않습니다.
    let person2 = person1.clone();

    println!("{:?}", person1);
    println!("{:?}", person2);
}

/*실행결과
Person { name: "루나", age: 10, cloned: false }
Person { name: "루나", age: 10, cloned: true }
*/

3. Drop 예제
// 객체가 메모리에서 벗어날 때 수행해야 할 작업 지정
struct Book {
    title: String,
}

impl Drop for Book {
    // Drop트레잇을 구현합니다.
    fn drop(&mut self) {
        println!("Book객체 해제: {}", self.title);
    }
}

fn main() {
    {
        let book = Book { title: String::from("러스트") };
    } // book의 Drop트레잇이 자동으로 호출됩니다.
}

/*실행결과
Book객체 해제: 러스트
*/