ライフタイム - 呪力の持続時間
ライフタイムとは - 術式の有効期間
ライフタイムは参照がいつまで有効なのかを示すものだ。俺の術式にも発動時間があるように、参照にも「生きている期間」がある。
大抵の場合、Rustのコンパイラが自動で推論してくれるが、時には明示的に指定する必要がある。これがライフタイム注釈だ。
五条先生の解説
ライフタイム注釈は 'a
のような形で書く。これは「この参照はライフタイム'aの間有効」という意味だ。 コンパイラがダングリングポインタ(無効な参照)を防ぐために使う。
ライフタイムの基本概念
基本的なライフタイム
fn main() {
let technique; // -------+-- 'a
// |
{ // |
let temp = String::from("蒼"); // -+-- 'b
technique = &temp; // | |
} // -------+ |
// |
// println!("{}", technique); // エラー!'b < 'a
} // ----------+
有効なライフタイム
fn main() {
let temp = String::from("術式順転『蒼』"); // ----+-- 'a
let technique = &temp; // |
println!("{}", technique); // |
} // ----+
関数のライフタイム注釈
基本的な注釈
// 入力と出力が同じライフタイム
fn get_longer<'a>(x: &'a str, y: &'a str) -> &'a str {
if x.len() > y.len() {
x
} else {
y
}
}
fn main() {
let technique1 = "蒼";
let technique2 = "術式順転『蒼』";
let longer = get_longer(technique1, technique2);
println!("長い方: {}", longer);
}
複数のライフタイム
// 異なるライフタイムを持つ参照
fn combine_techniques<'a, 'b>(
primary: &'a str,
secondary: &'b str
) -> String {
format!("{} + {}", primary, secondary)
}
fn main() {
let blue = "蒼";
let red = "赫";
let combo = combine_techniques(blue, red);
println!("コンボ: {}", combo);
}
構造体のライフタイム
ライフタイムを持つ構造体
// 参照を保持する構造体
struct TechniqueRef<'a> {
name: &'a str,
power: i32,
}
impl<'a> TechniqueRef<'a> {
fn new(name: &'a str, power: i32) -> Self {
TechniqueRef { name, power }
}
fn display(&self) {
println!("{}: 威力 {}", self.name, self.power);
}
// メソッドのライフタイム
fn get_name(&self) -> &'a str {
self.name
}
}
fn main() {
let technique_name = "無下限呪術";
let technique = TechniqueRef::new(technique_name, 9999);
technique.display();
println!("技名: {}", technique.get_name());
}
複雑な構造体
struct Sorcerer<'a> {
name: &'a str,
techniques: Vec<&'a str>,
power: i32,
}
impl<'a> Sorcerer<'a> {
fn new(name: &'a str) -> Self {
Sorcerer {
name,
techniques: Vec::new(),
power: 1000,
}
}
fn add_technique(&mut self, technique: &'a str) {
self.techniques.push(technique);
self.power += 200;
}
// 最強の技を返す
fn get_strongest_technique(&self) -> Option<&'a str> {
// 簡易的な判定
if self.techniques.iter().any(|&t| t.contains("紫")) {
self.techniques.iter().find(|&&t| t.contains("紫")).copied()
} else if self.techniques.iter().any(|&t| t.contains("茈")) {
self.techniques.iter().find(|&&t| t.contains("茈")).copied()
} else {
self.techniques.first().copied()
}
}
// 戦闘力評価
fn evaluate_combat_power(&self) -> String {
match self.techniques.len() {
0 => "初心者".to_string(),
1..=2 => "中級者".to_string(),
3..=4 => "上級者".to_string(),
_ => "最強候補".to_string(),
}
}
}
fn main() {
// すべての文字列リテラルは'staticライフタイム
let mut gojo = Sorcerer::new("五条悟");
gojo.add_technique("術式順転『蒼』");
gojo.add_technique("術式反転『赫』");
gojo.add_technique("虚式『茈』");
gojo.add_technique("無下限呪術『紫』");
println!("呪術師: {}", gojo.name);
println!("呪力: {}", gojo.power);
println!("評価: {}", gojo.evaluate_combat_power());
if let Some(strongest) = gojo.get_strongest_technique() {
println!("最強技: {}", strongest);
}
}
ライフタイム省略規則
Rustには3つの省略規則がある:
規則1: 各パラメータに独自のライフタイム
// これは...
fn analyze(text: &str) -> usize {
text.len()
}
// 実際はこう解釈される
fn analyze_explicit<'a>(text: &'a str) -> usize {
text.len()
}
規則2: 単一の入力ライフタイムが出力に適用
// これは...
fn get_first_word(text: &str) -> &str {
text.split_whitespace().next().unwrap_or("")
}
// 実際はこう解釈される
fn get_first_word_explicit<'a>(text: &'a str) -> &'a str {
text.split_whitespace().next().unwrap_or("")
}
規則3: &selfがある場合、そのライフタイムが出力に適用
struct Technique {
name: String,
}
impl Technique {
// これは...
fn get_name(&self) -> &str {
&self.name
}
// 実際はこう解釈される
fn get_name_explicit<'a>(&'a self) -> &'a str {
&self.name
}
}
静的ライフタイム
'static ライフタイム
// 文字列リテラルは'staticライフタイム
fn get_default_technique() -> &'static str {
"基本術式" // プログラム終了まで有効
}
// 静的変数
static ULTIMATE_TECHNIQUE: &str = "無下限呪術『紫』";
fn main() {
let default = get_default_technique();
println!("デフォルト: {}", default);
println!("究極技: {}", ULTIMATE_TECHNIQUE);
}
Boxとライフタイム
// ヒープ上のデータは所有権で管理
fn create_owned_technique() -> Box<str> {
let technique = String::from("術式順転『蒼』");
technique.into_boxed_str()
}
fn main() {
let owned = create_owned_technique();
println!("所有された技: {}", owned);
}
実践例 - 呪術戦闘記録システム
use std::collections::HashMap;
// 戦闘記録
struct BattleRecord<'a> {
combatants: Vec<&'a str>,
techniques_used: Vec<&'a str>,
winner: Option<&'a str>,
duration: u32,
}
impl<'a> BattleRecord<'a> {
fn new() -> Self {
BattleRecord {
combatants: Vec::new(),
techniques_used: Vec::new(),
winner: None,
duration: 0,
}
}
fn add_combatant(&mut self, name: &'a str) {
self.combatants.push(name);
}
fn record_technique(&mut self, technique: &'a str) {
self.techniques_used.push(technique);
}
fn set_winner(&mut self, winner: &'a str) {
if self.combatants.contains(&winner) {
self.winner = Some(winner);
}
}
fn set_duration(&mut self, duration: u32) {
self.duration = duration;
}
// 戦闘統計を返す
fn get_statistics(&self) -> BattleStats<'a> {
let mut technique_count = HashMap::new();
for &technique in &self.techniques_used {
*technique_count.entry(technique).or_insert(0) += 1;
}
BattleStats {
record: self,
technique_frequency: technique_count,
}
}
}
// 戦闘統計(元の記録への参照を保持)
struct BattleStats<'a> {
record: &'a BattleRecord<'a>,
technique_frequency: HashMap<&'a str, i32>,
}
impl<'a> BattleStats<'a> {
fn most_used_technique(&self) -> Option<&'a str> {
self.technique_frequency
.iter()
.max_by_key(|(_, &count)| count)
.map(|(&technique, _)| technique)
}
fn generate_report(&self) -> String {
let mut report = String::new();
report.push_str("=== 戦闘レポート ===\n");
report.push_str(&format!("参加者: {:?}\n", self.record.combatants));
report.push_str(&format!("継続時間: {}分\n", self.record.duration));
if let Some(winner) = self.record.winner {
report.push_str(&format!("勝者: {}\n", winner));
} else {
report.push_str("勝者: 未決定\n");
}
report.push_str(&format!("使用技数: {}\n", self.record.techniques_used.len()));
if let Some(most_used) = self.most_used_technique() {
let count = self.technique_frequency[most_used];
report.push_str(&format!("最多使用技: {} ({}回)\n", most_used, count));
}
report.push_str("\n=== 技使用履歴 ===\n");
for (i, technique) in self.record.techniques_used.iter().enumerate() {
report.push_str(&format!("{}. {}\n", i + 1, technique));
}
report
}
}
// 戦闘シミュレーター
struct BattleSimulator;
impl BattleSimulator {
fn simulate_gojo_vs_sukuna<'a>() -> BattleRecord<'a> {
let mut record = BattleRecord::new();
record.add_combatant("五条悟");
record.add_combatant("両面宿儺");
// 戦闘シミュレーション
record.record_technique("術式順転『蒼』");
record.record_technique("解");
record.record_technique("術式反転『赫』");
record.record_technique("捌");
record.record_technique("虚式『茈』");
record.record_technique("伏魔御廚子");
record.record_technique("無下限呪術『紫』");
record.record_technique("領域展開・無量空処");
record.set_winner("五条悟");
record.set_duration(15);
record
}
fn simulate_student_battle<'a>(student1: &'a str, student2: &'a str) -> BattleRecord<'a> {
let mut record = BattleRecord::new();
record.add_combatant(student1);
record.add_combatant(student2);
record.record_technique("基本術式");
record.record_technique("呪具操作");
record.record_technique("強化術式");
record.record_technique("防御術式");
// ランダムな勝者(簡易版)
record.set_winner(student1);
record.set_duration(8);
record
}
}
fn main() {
println!("=== 呪術戦闘記録システム ===\n");
// 最強対決
let gojo_battle = BattleSimulator::simulate_gojo_vs_sukuna();
let stats = gojo_battle.get_statistics();
println!("{}", stats.generate_report());
// 学生戦闘
let student_battle = BattleSimulator::simulate_student_battle("虎杖悠仁", "伏黒恵");
let student_stats = student_battle.get_statistics();
println!("{}", student_stats.generate_report());
}
高度なライフタイム
ライフタイム境界
use std::fmt::Display;
// ライフタイム境界付きのジェネリック関数
fn display_longest<'a, T>(x: &'a T, y: &'a T) -> &'a T
where
T: Display + PartialOrd,
{
println!("比較中: {} vs {}", x, y);
if x > y { x } else { y }
}
fn main() {
let power1 = 1000;
let power2 = 1500;
let stronger = display_longest(&power1, &power2);
println!("より強い呪力: {}", stronger);
}
高階ライフタイム境界
// 関数ポインタのライフタイム
fn apply_to_technique<'a, F>(technique: &'a str, func: F) -> String
where
F: Fn(&str) -> String,
{
func(technique)
}
fn enhance_technique(technique: &str) -> String {
format!("強化された{}", technique)
}
fn main() {
let technique = "術式順転『蒼』";
let result = apply_to_technique(technique, enhance_technique);
println!("{}", result);
}
まとめ
ライフタイムの習得は完了だ!重要なポイント:
- ライフタイム注釈 -
'a
で参照の有効期間を明示 - 省略規則 - 多くの場合は自動推論
- 構造体のライフタイム - 参照を保持する構造体
- 'static - プログラム全体で有効な参照
- 借用チェッカー - ダングリングポインタを防止
これで所有権、借用、ライフタイムの三位一体をマスターした。俺の無下限術式のように、完璧な制御ができるようになったな。
「ライフタイムを理解すれば、参照の生死を自在に操れる」