Rust Iced でGUIアプリを作った 岡山大学電子計算機研究会 : 瀕死 @hinshiba 2025-12-06
自己紹介 2 ◼ ID: hinshiba ◼ Name: 柴田 晴 ◼ 岡山大学 電子計算機研究会 副部長 ◼ B2 旧Twitter & GitHub @hinshiba https://hinshiba.net/
活動 3
活動 4
活動 5
活動 6
活動 7 TUI 画像整理 ハッカソン Unityでレースゲーム numpyだけの 左手デバイス ニューラルネットワーク 電子ペーパー + サーボモーターの制御 NO IMG Discord bot セキュリティ・キャンプ コネクト2025 NO Cの標準ライブラリのみで独自言語 IMG 笠岡市との共同プロジェクト? 冬コミ C107での出店予定
GUI書いたことないじゃん 8 GUI書いたことないじゃん numpyだけの 左手デバイス TUI 画像整理 ハッカソン Unityでレースゲーム ニューラルネットワーク 電子ペーパー + サーボモーターの制御 NO IMG Discord bot セキュリティ・キャンプ コネクト2025 NO Cの標準ライブラリのみで独自言語 IMG 笠岡市との共同プロジェクト? 冬コミ C107での出店予定
Outline ◼ 制作物 ◼ Rustでライブラリ ◼ IcedによるGUI ◼ おわり 9
Outline ◼ 制作物 ◼ Rustでライブラリ ◼ IcedによるGUI ◼ おわり 10
製作物 11 ◼画像のタグ付け ◼バッチ処理 ◼操作記録機能
製作物 12 画像表示部
製作物 13 タグ表示部
製作物 14 画像操作 タグ操作
Outline ◼ 制作物 ◼ Rustでライブラリ ◼ IcedによるGUI ◼ おわり 15
設計 16 GUI CLI lib CLIをまず作ると考える
設計 17 GUI CLI lib CLIをまず作ると考える 共通部分をライブラリに
ワークスペースの利用 18 root ├─ tagup_lib │ └ src ├─ tagproc_cli │ └ src └─ tagedit_gui └ src 複数パッケージに分割
ライブラリの全体像 19
Tag型 20 Tag 型 単一のタグを示す #[derive(Clone, Debug, Hash, PartialEq, Eq)] pub struct Tag(String); newtypeパターン
Tags型 21 Tags 型 タグの列を示す #[derive(…略)] pub struct Tags(Vec<Tag>); ここでも新しい型
トレイトの活用 新しい型 ◼コンストラクタが必要 ◼メソッドの不足 22
トレイトの活用 新しい型 ◼コンストラクタが必要 From系トレイト ◼メソッドの不足 Derefトレイト AsRefトレイト 23
From系トレイト
型変換を行う
impl From<Vec<Tag>> for Tags { // 略 }
Tags::from(vec![tag])
みたいなことが
できるようになる
24
種類
◼From<T>
◼FromStr
◼&strから変換
◼str::parse()
◼TryFrom<T>
◼失敗する可能性
◼Self::try_from()
◼Result<Self, Err>
Derefトレイト
25
自動で型変換
impl Deref for Tags {
type Target = Vec<Tag>;
// 略 }
let v: &Vec<Tag> = &*&tags;
& 演算子が &Tags
に
tags.iter()
* 演算子が Vec<Tag> に
みたいなことが
できるようになる
& 演算子が &Vec<Tag> に
AsRefトレイト 参照を作成する impl AsRef<str> for Tag { // 略 } tag.as_ref().trim() みたいなことが できるようになる 26 種類 ◼AsRef<T> ◼&Tを返す ◼AsMut<T> ◼&mut Tを返す
型変換マップ 型の恩恵 可読性向上 型検査 27
イテレータチェーン
let changes: TagsChanges = tags
.into_iter()
.map(TagChanges::new)
.replace(&replace_dict)
.delete(&delete_set)
.prepend(prepend_tags.into())
.append(append_tags.into())
.unique()
.absorb()
.collect();
28
◼イテレータを繋げる
◼型が違うので大変
map() -> Map<Self, F>
chain() -> Chain<Self, U::IntoIter>
etc
◼カッコイイ
テスト 29
テスト 30
Outline ◼ 制作物 ◼ Rustでライブラリ ◼ IcedによるGUI ◼ おわり 31
Icedの思想 32 ◼Elmアーキテクチャに着想 ◼関数型プログラミング言語 ◼JavaScriptにトランスパイル ◼Simple (easyでない) https://book.iced.rs/architecture.html
Elmアーキテクチャの利用 分割を強制 https://book.iced.rs/architecture.html ◼State ◼表示する状態 ◼Update ◼状態をどう変化させるか ◼View (dictates) ◼状態をどう表示するか 33
Stateの作成
struct TagEdit {
image_paths: Vec<PathBuf>,
current_image: image::Handle,
current_image_idx: usize,
// ......
}
◼表示すべき画像一覧
◼今の画像のハンドラ
◼今の画像の番号
34
Viewの作成
impl TagEdit {
pub fn view(&self) -> Element<'_, Message> {
widget::column![
image(self.current_image.clone()),
button("next")
]
.into()
}
◼StateからWidgetsを作成
◼image
◼button
35
エントリポイント
fn main() -> anyhow::Result<()> {
iced::application(“tagedit gui”,
TagEdit::update,
TagEdit::view
).run_with(TagEdit::init)?;
Ok(())
}
◼iced::application()に渡すだけ
◼.run_with()で初期状態を渡す
◼update()は空でもよい
36
実行例 37
Messageの作成 #[derive(Debug, Clone)] pub enum ImageUpdateMessage { Next, Prev, } ◼ただのenum型 38 button("next").on_press(ImageUpdateMessage::Next) ◼button()に追加
Updateの作成
impl TagEdit {
fn update_image(&mut self, msg: ImageUpdateMessage) {
match msg {
ImageUpdateMessage::Next => self.current_image_idx += 1,
};
self.current_image =
image::Handle::from_path(self.image_paths[self.current_image_idx].clone());
}
}
◼current_image_idxをインクリメント
◼画像を再読み込み
39
実践 40
Outline ◼ 制作物 ◼ Rustでライブラリ ◼ IcedによるGUI ◼ おわり 41
Rust Iced でGUIアプリを作った 岡山大学電子計算機研究会 : 瀕死 @hinshiba 2025-12-06 ご清聴ありがとうございました