빌더 패턴
객체를 단계별로 구성해 보세요.
빌더 패턴은(는) CoddyKit의 무료 Learn Rust Coding 강의입니다. 이것은 4개 중 1번째 강의입니다. 아래에서 전체 강의를 무료로 읽을 수 있으며, 내장 코드 에디터와 24/7 AI 튜터와 함께 브라우저에서 직접 실습할 수 있습니다. 이 강의는 Learn Rust Coding 학습 경로의 일부이며, 진행 상황이 웹과 CoddyKit 앱에 동기화됩니다. Learn Rust Coding 강의에는 총 4개의 강의가 포함되어 있습니다.
이 강의의 일부는 아직 번역되지 않았으며 영어로 표시됩니다.
Why a Builder?
Rust has no named or optional function arguments. When a struct has many fields, especially optional ones, a constructor with eight positional parameters becomes unreadable and error-prone.
The builder pattern solves this. You configure an object step by step with named methods, then call a final build() to produce the value. It reads like a fluent sentence.
The Target Struct
Start with the type you actually want to construct. Here a server configuration carries a required host plus several optional knobs.
Notice the fields are private to encourage construction through the builder rather than struct literals.
pub struct ServerConfig {
host: String,
port: u16,
max_connections: usize,
use_tls: bool,
}A Separate Builder Type
The classic approach uses a second struct, the builder. It mirrors the target but stores work-in-progress state. Optional fields often become Option<T> so you can tell "unset" from "explicitly set".
pub struct ServerConfigBuilder {
host: String,
port: Option<u16>,
max_connections: Option<usize>,
use_tls: bool,
}Starting the Builder
Give the builder a constructor that takes only the required fields. Everything optional starts as None or a default.
A common convention is a builder() method on the target type that returns the builder.
impl ServerConfig {
pub fn builder(host: impl Into<String>) -> ServerConfigBuilder {
ServerConfigBuilder {
host: host.into(),
port: None,
max_connections: None,
use_tls: false,
}
}
}Setter Methods Take self by Value
Each setter consumes self, mutates a field, and returns self. Returning the owned value lets you chain calls fluently.
This ownership-based chaining is the idiomatic Rust style and avoids lifetime headaches.
impl ServerConfigBuilder {
pub fn port(mut self, port: u16) -> Self {
self.port = Some(port);
self
}
pub fn use_tls(mut self, yes: bool) -> Self {
self.use_tls = yes;
self
}
}Applying Defaults in build()
The terminal build() turns the builder into the real type. This is where you fill in defaults for anything still None using unwrap_or.
impl ServerConfigBuilder {
pub fn build(self) -> ServerConfig {
ServerConfig {
host: self.host,
port: self.port.unwrap_or(8080),
max_connections: self.max_connections.unwrap_or(128),
use_tls: self.use_tls,
}
}
}Fluent Construction
Now construction reads top to bottom. Required data goes into builder(); each optional tweak is a clearly named call.
Fields you skip silently take their defaults.
fn main() {
let cfg = ServerConfig::builder("localhost")
.port(9000)
.use_tls(true)
.build();
println!("{}:{} tls={}", cfg.host, cfg.port, cfg.use_tls);
}A Complete Runnable Example
Here is the whole pattern condensed into one program you can run. It shows that skipped fields fall back to defaults inside build().
struct Config { name: String, retries: u32 }
struct Builder { name: String, retries: Option<u32> }
impl Config {
fn builder(name: &str) -> Builder {
Builder { name: name.to_string(), retries: None }
}
}
impl Builder {
fn retries(mut self, n: u32) -> Self { self.retries = Some(n); self }
fn build(self) -> Config {
Config { name: self.name, retries: self.retries.unwrap_or(3) }
}
}
fn main() {
let c = Config::builder("job").build();
println!("{} retries={}", c.name, c.retries);
}Fallible build with Result
Sometimes a configuration can be invalid, for example a port of zero. Make build() return Result so validation failures surface as recoverable errors instead of panics.
impl ServerConfigBuilder {
pub fn try_build(self) -> Result<ServerConfig, String> {
let port = self.port.unwrap_or(8080);
if port == 0 {
return Err("port must be non-zero".into());
}
Ok(ServerConfig { host: self.host, port,
max_connections: self.max_connections.unwrap_or(128),
use_tls: self.use_tls })
}
}The derive_builder Crate
Writing builders by hand is repetitive. The derive_builder crate generates the whole builder from an annotation.
You annotate fields with defaults and get a generated FooBuilder with setters and a fallible build() for free.
use derive_builder::Builder;
#[derive(Builder)]
struct Channel {
#[builder(default = "8080")]
port: u16,
name: String,
}Owned vs Mutable-Reference Builders
Two styles exist. The owned style consumes self and chains naturally. The &mut self style returns &mut Self and lets you split building across statements without re-binding.
Owned is more common for one-shot construction; the mutable style suits conditional configuration in loops.
impl ServerConfigBuilder {
pub fn port_ref(&mut self, port: u16) -> &mut Self {
self.port = Some(port);
self
}
}Quick Check
Test your understanding of the owned-self builder style.
Recap
The builder pattern works around Rust's lack of optional arguments. A builder type holds work-in-progress state, setters consume and return self for chaining, and build() applies defaults to produce the final value.
Use Result from build() for validation, and reach for derive_builder to skip the boilerplate.
자주 묻는 질문
“빌더 패턴” 강의는 무료인가요?
네 — “빌더 패턴” 전체 내용을 이 웹사이트에서 무료로 읽을 수 있습니다. 인터랙티브하게 실습하려면(내장 코드 에디터와 24/7 AI 튜터), CoddyKit PRO로 업그레이드하면 Learn Rust Coding 강의 전체를 잠금 해제할 수 있습니다. Learn Rust Coding 강의에는 총 4개의 강의가 포함되어 있습니다.
“빌더 패턴”에서 뭘 배우나요?
객체를 단계별로 구성해 보세요. 브라우저에서 직접 실행하는 실습 코드로 Learn Rust Coding을(를) 배우며, 24/7 AI 튜터가 강의를 진행하면서 질문에 답변해줍니다.
Learn Rust Coding을(를) 시작하는 데 경험이 필요한가요?
사전 경험은 필요하지 않습니다. CoddyKit의 Learn Rust Coding은(는) 초급자부터 고급 학습자까지를 위해 구성되어 있으므로, 여기서 시작하거나 처음부터 시작할 수 있으며 자신의 속도대로 진행할 수 있습니다. 이것은 4개 중 1번째 강의입니다.
“빌더 패턴” 강의는 얼마나 걸리나요?
대부분의 CoddyKit 강의는 약 5~10분이 소요됩니다. 각 강의는 간결하고 인터랙티브하여 꾸준한 진행이 가능하며, 웹과 앱에서 중단한 부분부터 바로 시작할 수 있습니다.
이 Learn Rust Coding 강의에서 코드를 작성하고 실행할 수 있나요?
네. 모든 Learn Rust Coding 강의에는 내장 코드 에디터가 포함되어 있으므로, 브라우저에서 바로 실제 코드를 작성하고 실행한 후 즉시 AI 피드백을 받을 수 있습니다 — 로컬 설정이 필요 없습니다.