# Rust 기초: 경험 있는 개발자를 위한 2026년 가이드 > C++, Java, Python 경험을 활용하여 Rust를 효율적으로 학습하는 가이드입니다. 소유권, 빌림, 라이프타임 등 Rust 핵심 개념을 체계적으로 설명합니다. - Published: 2026-01-09 - Updated: 2026-04-01 - Author: Anthony Fillion-Maillet - Tags: rust, systems programming, ownership, memory safety, performance - Reading time: 14 min --- Rust는 해마다 채택률이 증가하고 있으며, 컴파일 타임에 메모리 안전성을 보장하면서 C++ 수준의 성능을 제공하고 현대적인 에코시스템을 갖춘 언어로 자리 잡았습니다. C++, Java, Python 등의 경험이 있는 개발자에게 Rust 학습은 처음에 다소 낯설 수 있지만, 핵심 개념을 이해하면 빠르게 직관적으로 다가옵니다. > **2026년, 왜 Rust인가** > > Rust는 Stack Overflow에서 8년 연속 '가장 사랑받는 언어'로 선정되었습니다. Microsoft, Google, Amazon, Meta가 핵심 컴포넌트에 도입하고 있으며, 가비지 컬렉터 없이 메모리 안전성을 달성하는 유일한 주요 언어입니다. ## Rust 개발 환경 설정 코드를 작성하기 전에 공식 버전 관리 도구인 rustup을 통해 Rust를 설치해야 합니다. ```bash # install.sh # Install Rust via rustup (macOS, Linux) curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh # Verify installation rustc --version cargo --version ``` Cargo는 Rust의 패키지 매니저이자 빌드 도구입니다. npm, Maven, Make의 기능을 하나의 통합 도구로 결합한 것이라고 볼 수 있습니다. ```bash # project-setup.sh # Create a new project cargo new my_project cd my_project # Generated structure: # my_project/ # ├── Cargo.toml # Manifest (like package.json) # └── src/ # └── main.rs # Entry point # Essential commands cargo build # Compile the project cargo run # Compile and run cargo test # Run tests cargo check # Check without building (faster) ``` ## 변수와 기본 불변성 Rust는 일반적인 언어와 반대 접근법을 취합니다. 변수는 **기본적으로 불변(immutable)**입니다. 이 설계는 가변성에 대해 명시적으로 사고하도록 강제하며, 많은 버그를 사전에 방지합니다. ```rust // variables.rs fn main() { // Immutable by default let x = 5; // x = 6; // Compile error! // Explicitly mutable variable let mut y = 5; y = 6; // OK // Shadowing: redeclaration in the same scope let x = x + 1; // Creates a new variable x let x = x * 2; // x is now 12 // Shadowing also allows changing the type let spaces = " "; // &str let spaces = spaces.len(); // usize } ``` > **섀도잉과 가변성의 차이** > > 섀도잉은 새로운 변수를 생성하는 반면, `mut`는 기존 값을 수정합니다. 섀도잉을 사용하면 명확한 이름을 유지하면서 값을 변환할 수 있습니다. ## 기본 데이터 타입 Rust는 강력한 타입 추론 기능을 갖춘 정적 타입 언어입니다. 반드시 알아야 할 기본 프리미티브 타입은 다음과 같습니다. ```rust // types.rs fn main() { // Signed integers: i8, i16, i32, i64, i128, isize let age: i32 = 30; // Unsigned integers: u8, u16, u32, u64, u128, usize let count: u64 = 1_000_000; // Underscores for readability // Floats: f32, f64 (default) let pi: f64 = 3.14159; // Boolean let active: bool = true; // Unicode character (4 bytes) let emoji: char = '🦀'; // Tuple: fixed collection of different types let person: (String, i32) = (String::from("Alice"), 28); let (name, age) = person; // Destructuring // Array: fixed size, same type let numbers: [i32; 5] = [1, 2, 3, 4, 5]; let first = numbers[0]; // Slice: view into a portion of data let slice: &[i32] = &numbers[1..4]; // [2, 3, 4] } ``` ## 소유권: Rust의 혁신적 개념 소유권(Ownership)은 Rust의 가장 핵심적인 혁신입니다. 가비지 컬렉터 없이 메모리 안전성을 보장하는 시스템이며, 초기 학습 비용이 있지만 그 가치는 충분합니다. ### 소유권의 세 가지 규칙 ```rust // ownership.rs fn main() { // Rule 1: Each value has a single owner let s1 = String::from("hello"); // Rule 2: When the owner goes out of scope, the value is freed { let s2 = String::from("world"); // s2 is valid here } // s2 is dropped, no longer accessible // Rule 3: Only one ownership at a time (move) let s3 = s1; // s1 is MOVED to s3 // println!("{}", s1); // Error: s1 is no longer valid! println!("{}", s3); // OK: s3 is the owner } ``` ### 이동(Move)과 복제(Clone) ```rust // move_clone.rs fn main() { // Simple types (stack): automatic Copy let x = 5; let y = x; // Copy, not move println!("x = {}, y = {}", x, y); // Both are valid // Complex types (heap): Move by default let s1 = String::from("hello"); let s2 = s1; // Move // s1 is no longer usable // Explicit clone to duplicate let s3 = String::from("world"); let s4 = s3.clone(); // Deep copy println!("s3 = {}, s4 = {}", s3, s4); // Both valid } ``` > **이동과 함수 호출** > > 값을 함수에 전달하면 소유권이 이동합니다. 함수가 소유자가 되며, 호출 이후에는 해당 값에 접근할 수 없습니다. 반환값으로 돌려받지 않는 한 원래 스코프에서 사용이 불가능합니다. ```rust // ownership_functions.rs fn main() { let s = String::from("hello"); takes_ownership(s); // println!("{}", s); // Error: s has been moved let x = 5; makes_copy(x); println!("{}", x); // OK: i32 implements Copy // To regain ownership, return the value let s2 = String::from("hello"); let s3 = takes_and_gives_back(s2); println!("{}", s3); // OK } fn takes_ownership(s: String) { println!("{}", s); } // s is dropped here fn makes_copy(x: i32) { println!("{}", x); } fn takes_and_gives_back(s: String) -> String { s // Returns ownership } ``` ## 빌림: 소유권 이전 없는 참조 빌림(Borrowing)을 사용하면 소유권을 이전하지 않고 값을 활용할 수 있습니다. Rust에서 가장 자주 사용되는 메커니즘입니다. ```rust // borrowing.rs fn main() { let s1 = String::from("hello"); // Immutable reference: read-only let len = calculate_length(&s1); println!("Length of '{}': {}", s1, len); // s1 still valid // Mutable reference: modification allowed let mut s2 = String::from("hello"); change(&mut s2); println!("{}", s2); // "hello, world" } fn calculate_length(s: &String) -> usize { s.len() } // s goes out of scope but doesn't drop (it's a reference) fn change(s: &mut String) { s.push_str(", world"); } ``` ### 빌림의 규칙 ```rust // borrowing_rules.rs fn main() { let mut s = String::from("hello"); // Rule 1: Multiple immutable references simultaneously OK let r1 = &s; let r2 = &s; println!("{} and {}", r1, r2); // Rule 2: ONLY ONE mutable reference at a time let r3 = &mut s; // let r4 = &mut s; // Error: already borrowed mutably println!("{}", r3); // Rule 3: No mutable ref if immutable ref exists let r5 = &s; // let r6 = &mut s; // Error: r5 is still active println!("{}", r5); // Once r5 is used for the last time, mutable borrow is allowed let r7 = &mut s; // OK: r5 is no longer used after this r7.push_str("!"); } ``` ## 라이프타임: 참조 유효성 보장 라이프타임은 참조가 유효한 상태로 유지되도록 보장합니다. 컴파일러가 대부분 자동으로 추론하지만, 명시적 어노테이션이 필요한 경우도 있습니다. ```rust // lifetimes_basic.rs // Classic error: reference to freed data // fn dangling() -> &String { // let s = String::from("hello"); // &s // Error: s will be dropped, invalid reference! // } // Solution: return the owned value fn no_dangle() -> String { let s = String::from("hello"); s // Ownership transferred, no problem } ``` ### 라이프타임 어노테이션 ```rust // lifetimes_annotation.rs // The compiler can't figure out which reference will be returned // fn longest(x: &str, y: &str) -> &str { ... } // Error! // Explicit annotation: return lives as long as BOTH x AND y fn longest<'a>(x: &'a str, y: &'a str) -> &'a str { if x.len() > y.len() { x } else { y } } fn main() { let string1 = String::from("long string"); let result; { let string2 = String::from("xyz"); result = longest(&string1, &string2); println!("Longest: {}", result); // OK here } // println!("{}", result); // Error: string2 dropped } ``` > **라이프타임 생략 규칙** > > Rust에는 라이프타임 생략 규칙(Elision Rules)이 있어 단순한 경우에는 어노테이션을 생략할 수 있습니다. 초보자의 경우 컴파일러의 에러 메시지를 따르는 것만으로도 충분히 대응 가능합니다. ## 구조체와 메서드 구현 구조체(Struct)는 Rust에서 사용자 정의 타입을 만드는 기본 구성 요소입니다. ```rust // structs.rs // Struct definition #[derive(Debug)] // Enables printing with {:?} struct User { username: String, email: String, active: bool, sign_in_count: u64, } // Implementation block for methods impl User { // Constructor (convention: fn new or descriptive name) fn new(username: String, email: String) -> Self { Self { username, email, active: true, sign_in_count: 1, } } // Method: takes &self (reference to instance) fn is_active(&self) -> bool { self.active } // Method with mutation: takes &mut self fn deactivate(&mut self) { self.active = false; } // Method consuming self (rare) fn into_username(self) -> String { self.username } } fn main() { let mut user = User::new( String::from("alice"), String::from("alice@example.com"), ); println!("Active: {}", user.is_active()); user.deactivate(); println!("Active: {}", user.is_active()); // Debug print println!("{:?}", user); } ``` ## 열거형과 패턴 매칭 Rust의 열거형(Enum)은 대부분의 언어에서 제공하는 enum보다 훨씬 강력합니다. 각 배리언트가 데이터를 포함할 수 있습니다. ```rust // enums.rs // Simple enum enum Direction { North, South, East, West, } // Enum with data (algebraic data type) enum Message { Quit, Move { x: i32, y: i32 }, Write(String), ChangeColor(u8, u8, u8), } impl Message { fn process(&self) { match self { Message::Quit => println!("Quitting"), Message::Move { x, y } => println!("Moving to ({}, {})", x, y), Message::Write(text) => println!("Writing: {}", text), Message::ChangeColor(r, g, b) => { println!("Color: rgb({}, {}, {})", r, g, b) } } } } fn main() { let msg = Message::Move { x: 10, y: 20 }; msg.process(); let msg2 = Message::Write(String::from("Hello Rust!")); msg2.process(); } ``` ### Option과 Result: 에러 처리의 기초 ```rust // option_result.rs use std::fs::File; use std::io::{self, Read}; fn main() { // Option: present or absent value (replaces null) let numbers = vec![1, 2, 3]; let first: Option<&i32> = numbers.first(); match first { Some(n) => println!("First: {}", n), None => println!("Empty list"), } // Utility methods let value = first.unwrap_or(&0); let doubled = first.map(|n| n * 2); // Result: success or error let result = read_file("config.txt"); match result { Ok(content) => println!("Content: {}", content), Err(e) => println!("Error: {}", e), } } fn read_file(path: &str) -> Result { let mut file = File::open(path)?; // ? propagates the error let mut content = String::new(); file.read_to_string(&mut content)?; Ok(content) } ``` > **? 연산자** > > `?` 연산자는 에러 전파를 위한 문법적 설탕입니다. Result가 `Err`인 경우 자동으로 에러를 반환하고, `Ok`인 경우 내부 값을 꺼냅니다. ## 트레이트: Rust의 다형성 트레이트는 공통 동작을 정의하는 메커니즘으로, Java의 인터페이스나 Swift의 프로토콜과 유사한 역할을 합니다. ```rust // traits.rs // Trait definition trait Summary { fn summarize(&self) -> String; // Method with default implementation fn preview(&self) -> String { format!("{}...", &self.summarize()[..50.min(self.summarize().len())]) } } struct Article { title: String, author: String, content: String, } struct Tweet { username: String, content: String, } // Implementation for Article impl Summary for Article { fn summarize(&self) -> String { format!("{} by {}", self.title, self.author) } } // Implementation for Tweet impl Summary for Tweet { fn summarize(&self) -> String { format!("@{}: {}", self.username, self.content) } } // Function accepting any type implementing Summary fn notify(item: &impl Summary) { println!("Breaking news: {}", item.summarize()); } // Equivalent syntax with trait bound fn notify_generic(item: &T) { println!("Breaking news: {}", item.summarize()); } fn main() { let article = Article { title: String::from("Rust 2026"), author: String::from("Community"), content: String::from("..."), }; let tweet = Tweet { username: String::from("rustlang"), content: String::from("Rust is awesome!"), }; notify(&article); notify(&tweet); } ``` ## 표준 라이브러리의 주요 컬렉션 Rust 표준 라이브러리에는 강력한 컬렉션 타입이 제공됩니다. ```rust // collections.rs use std::collections::HashMap; fn main() { // Vec: dynamic array let mut numbers: Vec = Vec::new(); numbers.push(1); numbers.push(2); numbers.push(3); // vec! macro for initialization let nums = vec![1, 2, 3, 4, 5]; // Iteration for n in &nums { println!("{}", n); } // Functional methods let doubled: Vec = nums.iter().map(|x| x * 2).collect(); let sum: i32 = nums.iter().sum(); let evens: Vec<&i32> = nums.iter().filter(|x| *x % 2 == 0).collect(); // String: growable UTF-8 string let mut s = String::from("Hello"); s.push_str(", World!"); s.push('!'); // Concatenation let s1 = String::from("Hello, "); let s2 = String::from("World!"); let s3 = s1 + &s2; // s1 moved, s2 borrowed // or with format! let s4 = format!("{}{}", "Hello, ", "World!"); // HashMap let mut scores: HashMap = HashMap::new(); scores.insert(String::from("Blue"), 10); scores.insert(String::from("Red"), 50); // Access with get (returns Option) if let Some(score) = scores.get("Blue") { println!("Blue: {}", score); } // Entry API for conditional insertion scores.entry(String::from("Yellow")).or_insert(25); } ``` ## Rust의 관용적 에러 처리 올바른 에러 처리는 Rust 개발에서 필수적입니다. 다음은 권장되는 패턴입니다. ```rust // error_handling.rs use std::fs::File; use std::io::{self, Read}; use std::num::ParseIntError; // Define a custom error type #[derive(Debug)] enum AppError { Io(io::Error), Parse(ParseIntError), Custom(String), } // Implement From for automatic conversion impl From for AppError { fn from(err: io::Error) -> Self { AppError::Io(err) } } impl From for AppError { fn from(err: ParseIntError) -> Self { AppError::Parse(err) } } // Function returning Result with custom error fn read_number_from_file(path: &str) -> Result { let mut file = File::open(path)?; // io::Error -> AppError let mut content = String::new(); file.read_to_string(&mut content)?; let number: i32 = content.trim().parse()?; // ParseIntError -> AppError Ok(number) } fn main() { match read_number_from_file("number.txt") { Ok(n) => println!("Number: {}", n), Err(AppError::Io(e)) => println!("IO error: {}", e), Err(AppError::Parse(e)) => println!("Parse error: {}", e), Err(AppError::Custom(msg)) => println!("Error: {}", msg), } } ``` > **추천 크레이트** > > 실제 프로젝트에서는 `thiserror` 크레이트(라이브러리용)와 `anyhow` 크레이트(애플리케이션용)를 활용하면 에러 처리 구현을 크게 간소화할 수 있습니다. ## Rust의 테스트 기능 Rust는 테스트 프레임워크를 언어 자체에 내장하고 있습니다. 외부 라이브러리 없이 바로 테스트를 작성할 수 있습니다. ```rust // lib.rs pub fn add(a: i32, b: i32) -> i32 { a + b } pub fn divide(a: i32, b: i32) -> Result { if b == 0 { Err(String::from("Division by zero")) } else { Ok(a / b) } } // Test module (compiled only for `cargo test`) #[cfg(test)] mod tests { use super::*; #[test] fn test_add() { assert_eq!(add(2, 3), 5); } #[test] fn test_add_negative() { assert_eq!(add(-1, 1), 0); } #[test] fn test_divide_success() { assert_eq!(divide(10, 2), Ok(5)); } #[test] fn test_divide_by_zero() { assert!(divide(10, 0).is_err()); } #[test] #[should_panic(expected = "index out of bounds")] fn test_panic() { let v = vec![1, 2, 3]; let _ = v[99]; // Panic! } } ``` ## 결론 Rust는 메모리 안전성과 성능을 동시에 달성하는 독자적인 패러다임을 제공합니다. 소유권과 빌림 개념은 처음에는 제약처럼 느껴지지만, 실전 경험이 쌓이면 자연스러워집니다. Rust 컴파일러는 강력한 동반자이며, 업계 최고 수준의 에러 메시지를 제공합니다. ### Rust 학습 체크리스트 - rustup으로 Rust를 설치하고 Cargo 기본 명령어를 숙달합니다 - 불변성과 mut를 통한 명시적 가변성의 차이를 이해합니다 - 소유권의 세 가지 규칙을 확실히 익힙니다 - `&`와 `&mut` 참조를 사용한 빌림을 실전에서 연습합니다 - null과 예외 대신 `Option`과 `Result`를 활용합니다 - `#[test]` 어트리뷰트로 테스트를 작성합니다 Rust 커뮤니티는 협력적이며 학습 자료도 풍부합니다. 공식 문서인 'The Rust Programming Language'는 온라인에서 무료로 제공됩니다. 이 가이드에서 다룬 기초를 탄탄히 다진 후, async/await, 매크로, WebAssembly 등 고급 주제로 나아가는 것을 권장합니다. --- Source: SharpSkill (https://sharpskill.dev), tech interview preparation for your real stack. HTML version of this page: https://sharpskill.dev/ko/blog/rust/rust-basics-experienced-developers