Rustプログラミング言語を学び始めると、多くのコード例で構造体を生成する際にnewという名前の関数が使われていることに気づくでしょう。
Rustには他のオブジェクト指向言語にあるような専用の「コンストラクタ」という言語機能は存在しません。
その代わりに、構造体に関連付けられた関数、すなわち「関連関数」を定義することで、インスタンスの生成処理を実装するのがRustの標準的な作法です。
本記事では、このnew関数を中心とした関連関数の定義方法から、Rustコミュニティで推奨されている命名規則、そして保守性の高いコードを書くためのベストプラクティスについて詳しく解説します。
2026年現在のモダンなRust開発においても、これらの基礎知識は効率的で安全なコードを書くための不可欠な土台となります。
Rustにおける関連関数とnewの基礎知識
Rustの関連関数とは、特定の型のインスタンスに関連付けられているものの、そのインスタンス自身を引数に取らない関数のことを指します。
他の言語における静的メソッド(static method)に近い概念だと考えると理解しやすいでしょう。
メソッドと関連関数の違い
Rustでは、implブロックの中に定義される関数には、大きく分けて「メソッド」と「関連関数」の2種類があります。
メソッドは、第一引数に&selfや&mut selfを取り、特定のインスタンスの状態を操作するために使用されます。
一方で関連関数は、第一引数にselfを取らず、型名を通じて直接呼び出されるのが特徴です。
構造体のインスタンスを初期化して返す処理は、まさにインスタンスが存在する前に実行される必要があるため、関連関数として実装されます。
new関数の慣習的な役割
Rustにおいて「new」という名前は言語のキーワードではなく、単なる慣習(コンベンション)です。
しかし、標準ライブラリを含むほぼすべてのRustプロジェクトにおいて、newはその型の「デフォルトのコンストラクタ」として機能することが期待されています。
開発者はType::new()というコードを見るだけで、それが新しいインスタンスを生成するための最も基本的な手段であると直感的に理解できます。
new関連関数の基本的な実装方法
それでは、実際に構造体に対してnew関連関数を実装するコード例を見ていきましょう。
以下の例では、ユーザー情報を保持する単純な構造体を作成し、その初期化処理を定義しています。
// ユーザー情報を表す構造体
struct User {
username: String,
email: String,
sign_in_count: u64,
active: bool,
}
impl User {
// インスタンスを生成して返す関連関数
pub fn new(username: String, email: String) -> Self {
// フィールドを初期化して構造体を返す
Self {
username,
email,
sign_in_count: 1,
active: true,
}
}
}
fn main() {
// 関連関数の呼び出し
let user1 = User::new(
String::from("rust_user"),
String::from("user@example.com")
);
println!("User: {}, Email: {}", user1.username, user1.email);
}
User: rust_user, Email: user@example.com
このコードにおいて、new関数内の戻り値の型として指定されているSelfは、そのimplブロックが対象としている型(この場合はUser)を指すキーワードです。
User::newのように、二重コロン(::)を用いて呼び出す点が、ドット演算子を用いるメソッド呼び出しとの大きな違いです。
Rustにおける命名規則のベストプラクティス
Rustでは、生成のバリエーションに応じてnew以外の名前を使い分けることが推奨されています。
これにより、コードを読み取る側は、その関数がどのような意図でインスタンスを生成しようとしているのかを明確に把握できます。
状況に応じた命名の使い分け
よく使われる命名パターンを以下の表にまとめました。
| 関数名 | 主な用途・意図 |
|---|---|
new | 最も一般的で基本的な初期化。引数が少ない、または無い場合が多い。 |
with_capacity | 内部バッファのメモリサイズをあらかじめ指定して生成する場合。 |
from_* | 他の型から変換して生成する場合(例:from_str)。 |
default | 引数を取らず、標準的な初期値で生成する場合。 |
try_new | 失敗する可能性がある生成処理で、Resultを返す場合。 |
例えば、Vec::with_capacity(10)のように、特定のパラメータを強調したい場合には、単なるnewよりも具体的な名前を付けるべきです。
これにより、APIの利用者は「この関数を使うとメモリ効率が最適化されるのだな」と即座に理解できます。
from関数とIntoトレイトの活用
Rustにはデータの型変換に関する強力なエコシステムがあり、「from」から始まる関連関数を実装する際は、Fromトレイトの実装を検討するのが定石です。
impl From<T> for MyTypeを定義すると、自動的にInto<MyType>も利用可能になり、ジェネリックなプログラミングにおいて非常に強力な武器となります。
new関数の実装時に考慮すべき詳細設計
単純にフィールドを埋めるだけの関数であっても、Rust特有の設計思想に基づいた配慮が必要です。
ここでは、実務レベルで求められる高度な実装のポイントを解説します。
Defaultトレイトとの一貫性
引数を一切取らないnew関数を定義する場合、同時にDefaultトレイトを実装することが強く推奨されます。
なぜなら、多くのジェネリックなライブラリやコンテナが、Default::default()を呼び出すことで型を初期化できることを期待しているからです。
struct Config {
port: u16,
debug_mode: bool,
}
impl Config {
pub fn new() -> Self {
Self::default()
}
}
impl Default for Config {
fn default() -> Self {
Self {
port: 8080,
debug_mode: false,
}
}
}
このように、new関数の内部でdefault()を呼び出す構造にすることで、ロジックの二重管理を防ぎつつ、Rustの慣習に則った使いやすいAPIを提供できます。
エラー処理を考慮した生成
バリデーションが必要なデータを扱う場合、new関数が必ずしも成功するとは限りません。
例えば、0から100までの値しか許容しない「パーセンテージ」を表す型を考えてみましょう。
このようなケースでは、不正な値でインスタンスが作られるのを防ぐため、Result型を返す実装にします。
pub struct Percentage(u8);
impl Percentage {
pub fn new(value: u8) -> Result<Self, String> {
if value > 100 {
return Err(String::from("値は100以下である必要があります"));
}
Ok(Self(value))
}
}
このパターンを適用することで、プログラムの他の場所では「この型が存在するなら、中身は必ず正しい」という前提(不変条件)を置くことができ、安全性が飛躍的に向上します。
const fnによるコンパイル時初期化
2026年現在のRust開発において重要性が増しているのが、const fnとしてのnewの実装です。
const fnとして定義された関連関数は、コンパイル時に計算を行うことができ、静的な変数(static変数)の初期化に使用できます。
struct AppInfo {
version: &'static str,
}
impl AppInfo {
// constキーワードを付与することで、コンパイル時実行が可能になる
pub const fn new(version: &'static str) -> Self {
Self { version }
}
}
static GLOBAL_INFO: AppInfo = AppInfo::new("1.0.0");
実行時のオーバーヘッドをゼロに抑え、プログラムの起動速度やメモリ効率を極限まで高めることができるため、可能な限りconst fn化を検討しましょう。
複雑な初期化にはBuilderパターンを
引数が多すぎる場合や、オプションのパラメータが多数存在する場合には、new関数の引数を増やすのではなく、Builderパターンの採用を検討してください。
Rustでは、関連関数から専用の「Builder」構造体を返し、メソッドチェーンで設定を組み上げた後に最終的なインスタンスを生成する手法が一般的です。
impl User {
// 関連関数としてbuilderを提供
pub fn builder() -> UserBuilder {
UserBuilder::default()
}
}
このように設計することで、User::new(arg1, arg2, arg3, arg4, arg5)といった、どの引数が何を表しているのか判別しにくいコード(コードの臭い)を回避できます。
まとめ
Rustにおけるnew関連関数は、単なるインスタンス生成以上の役割を担っています。
それは、型の不変条件を保証し、利用者に直感的なインターフェースを提供するための重要な窓口です。
適切な命名規則に従い、DefaultトレイトやResult型、さらにはconst fnを組み合わせることで、堅牢で効率的なライブラリやアプリケーションを構築できます。
今回解説したベストプラクティスを意識して、Rustらしい洗練されたコードを追求していきましょう。
一貫性のある初期化処理の実装は、プロジェクトの規模が大きくなるほど、その真価を発揮することになります。
