プロジェクト概要
このプロジェクトでは、ユーザーの入力を受け取り、それに基づいてタスクを管理するシンプルな「To-Doリスト」を構築します。このTo-Doリストは、ユーザーがタスクを追加、一覧表示、削除できる機能を備えた、簡単なコマンドラインアプリケーションです。Rustの基本的なデータ構造や関数、エラーハンドリングなどを効果的に使うことで、実際に役立つプログラムを作成します。
10.1 設計とプロジェクトの準備
10.1.1 必要な機能の洗い出し
To-Doリストアプリケーションに必要な機能は次のとおりです。
- タスクの追加: ユーザーが新しいタスクを入力し、リストに追加できる。
- タスクの表示: すべてのタスクを一覧表示できる。
- タスクの削除: 指定したタスクをリストから削除できる。
- アプリケーションの終了: ユーザーがプログラムを終了できる。
10.1.2 プロジェクトの設定
- Cargoプロジェクトの作成: 新しいプロジェクトフォルダを作成し、Cargoで初期化します。
cargo new todo_list cd todo_list
- src/main.rs: メインのコードはこのファイルに記述していきます。
10.1.3 初期設定
このプログラムでは標準入力からのユーザーの指示を取得するためにstd::io
クレートを使用し、タスクの管理にはVec<String>
を用います。以下がその初期コードです。
use std::io;
fn main() {
println!("To-Doリストアプリへようこそ!");
}
10.2 基本機能の実装
10.2.1 メインループの作成
アプリケーションはユーザーからの指示を待ち、入力に基づいて適切なアクションを実行します。これをメインループで実現します。
fn main() {
println!("To-Doリストアプリへようこそ!");
println!("コマンド: 'add', 'list', 'remove', 'exit' で操作してください。");
loop {
// ユーザー入力を受け取る
let mut input = String::new();
io::stdin().read_line(&mut input).expect("入力エラー");
// コマンドの処理
let command = input.trim();
match command {
"add" => add_task(),
"list" => list_tasks(),
"remove" => remove_task(),
"exit" => {
println!("アプリケーションを終了します。");
break;
}
_ => println!("無効なコマンドです。再入力してください。"),
}
}
}
ここで、loop
文によりユーザー入力を待ち続け、適切なコマンドが入力されると対応する関数が実行されます。
10.2.2 タスクを格納するデータ構造
タスクを格納するためにVec<String>
型の変数を使います。タスクをmain
関数の中で管理するため、main
のスコープに持たせることにします。
let mut tasks: Vec<String> = Vec::new();
10.3 タスク追加機能の実装
ユーザーからタスク内容を入力してもらい、それをtasks
リストに追加するadd_task
関数を実装します。
fn add_task(tasks: &mut Vec<String>) {
println!("追加するタスクを入力してください:");
let mut task = String::new();
io::stdin().read_line(&mut task).expect("入力エラー");
tasks.push(task.trim().to_string());
println!("タスクが追加されました。");
}
ポイント
&mut Vec<String>
という参照で受け取ることで、リストに新しいタスクを追加可能にしています。trim()
で余分な空白を取り除き、無駄なスペースを省きます。
10.4 タスク一覧表示機能の実装
次に、すべてのタスクを表示する関数list_tasks
を作成します。
fn list_tasks(tasks: &Vec<String>) {
if tasks.is_empty() {
println!("タスクはありません。");
} else {
println!("現在のタスク:");
for (i, task) in tasks.iter().enumerate() {
println!("{}: {}", i + 1, task);
}
}
}
ポイント
iter().enumerate()
を使用し、インデックス番号とタスク内容を同時に表示。is_empty()
を使ってタスクがない場合のエラーメッセージも出力しています。
10.5 タスク削除機能の実装
特定のタスクをリストから削除するremove_task
関数を追加します。
fn remove_task(tasks: &mut Vec<String>) {
println!("削除するタスクの番号を入力してください:");
let mut index_str = String::new();
io::stdin().read_line(&mut index_str).expect("入力エラー");
match index_str.trim().parse::<usize>() {
Ok(index) if index > 0 && index <= tasks.len() => {
tasks.remove(index - 1);
println!("タスクが削除されました。");
}
_ => println!("無効な番号です。"),
}
}
ポイント
parse::<usize>()
を使って、入力文字列を数値に変換。- 無効なインデックスの処理も
match
式で対応。
10.6 コードの改善とデバッグのポイント
- 冗長なコードの整理
- コマンドの受付と処理を
main
関数で一つにまとめるのではなく、サブ関数で分離することでコードが読みやすくなり、拡張しやすくなります。
- コマンドの受付と処理を
- ユーザー入力のバリデーション
- 各関数でユーザー入力を必ず検証し、エラーメッセージを出力します。これによりユーザーエラーを未然に防ぎます。
- エラーハンドリングの追加
- 入力に問題があった場合の処理も行い、ユーザーが操作を継続しやすいようにしました。
10.7 完成コード
最終的な完成コードは以下の通りです。
use std::io;
fn main() {
println!("To-Doリストアプリへようこそ!");
println!("コマンド: 'add', 'list', 'remove', 'exit' で操作してください。");
let mut tasks: Vec<String> = Vec::new();
loop {
let mut input = String::new();
io::stdin().read_line(&mut input).expect("入力エラー");
match input.trim() {
"add" => add_task(&mut tasks),
"list" => list_tasks(&tasks),
"remove" => remove_task(&mut tasks),
"exit" => {
println!("アプリケーションを終了します。");
break;
}
_ => println!("無効なコマンドです。再入力してください。"),
}
}
}
fn add_task(tasks: &mut Vec<String>) {
println!("追加するタスクを入力してください:");
let mut task = String::new();
io::stdin().read_line(&mut task).expect("入力エラー");
tasks.push(task.trim().to_string());
println!("タスクが追加されました。");
}
fn list_tasks(tasks: &Vec<String>) {
if tasks.is_empty() {
println!("タスクはありません。");
} else {
println!("現在のタスク:");
for (i, task) in tasks.iter().enumerate() {
println!("{}: {}", i + 1, task);
}
}
}
fn remove_task(tasks: &mut Vec<String>) {
println!("削除するタスクの番号を入力してください:");
let mut index_str = String::new();
io::stdin().read_line(&mut index_str).expect("入力エラー");
match index_str.trim().parse::<usize>() {
Ok(index) if index > 0 && index <= tasks.len() => {
tasks.remove(index - 1);
println!("タスクが削除されました。");
}
_ => println!("無効な番号です。"),
}
}
この章では、Rustの基本的な構文を活用した実用的なアプリケーションの構築方法と、ユーザー入力やエラーハンドリングの実装、そしてコードの改善ポイントを学びました。この経験により、実践的なRustのスキルがさらに身についたことでしょう。