🌟
[Rustのデザインパターン学習] Visitor [振る舞い]
はじめに
-
Foldパターンを投稿予定でしたが(GoFにはないパターンで十分理解できなかったため)Visitorパターンを学習することにしました。
- ↓FoldとVisitorは似ているとのこと
The fold pattern is similar to visitor but produces a new version of the visited data structure.
- ↓FoldとVisitorは似ているとのこと
-
前回学んだBuilderパターンを使用しています。
内容
- Visitorパターンを使い、オブジェクト{
Tags
,BookInfo
}に対してString, Vec<String>を得るメソッドを定義しています。
main.rs
#[macro_use]
extern crate derive_builder;
use std::vec;
#[derive(Debug,Builder,Clone)]
struct Tags {
#[builder(setter(custom))]
names:Vec<String>
}
impl TagsBuilder {
fn names(mut self, names:Vec<&str>)->TagsBuilder{
let mut new_vec:Vec<String> = Vec::new();
for n in names {
new_vec.push(n.to_string());
}
self.names = Some(new_vec);
self
}
}
#[derive(Debug,Builder)]
struct BookInfo {
#[builder(setter(custom))]
name:String,
tags:Tags,
}
impl BookInfoBuilder {
fn name(mut self, name:&str)->BookInfoBuilder{
self.name = Some(name.to_string());
self
}
}
trait Visitor<T> {
fn visit_title(title:&BookInfo)->T;
fn visit_tags(tags:&Tags)->T;
}
impl Visitor<String> for BookInfo {
fn visit_title(title:&BookInfo)-> String {
let t = &title.name;
t.to_string()
}
fn visit_tags(tags:&Tags)->String {
let mut tag_names = String::from("");
for n in tags.names.iter() {
tag_names += n;
tag_names += ", ";
}
tag_names[0..tag_names.len()-2].to_string()
}
}
impl Visitor<Vec<String>> for BookInfo {
fn visit_title(title:&BookInfo)-> Vec<String> {
let n = title.name.clone();
let titles = vec![n];
titles
}
fn visit_tags(tags:&Tags)->Vec<String> {
tags.names.clone()
}
}
fn print_debug(tags:&Tags, title:&BookInfo){
println!("{:#?}",tags);
println!("{:#?}",title);
}
fn print_visitor_example(tags:&Tags, bookinfo:&BookInfo){
let title:String = BookInfo::visit_title(&bookinfo);
let tag:String = BookInfo::visit_tags(&bookinfo.tags);
println!("{}",title);
println!("{}\n",tag);
let titles:Vec<String> = BookInfo::visit_title(&bookinfo);
let tags:Vec<String> = BookInfo::visit_tags(&bookinfo.tags);
println!("{:?}",titles);
println!("{:?}",tags);
}
fn main(){
let tags = TagsBuilder::default()
.names(vec!["rust","oreilly"])
.build()
.unwrap();
let bak_tag = tags.clone();
let oreilly_rust = BookInfoBuilder::default()
.name("Programming Rust, 2nd Edition")
.tags(tags)
.build()
.unwrap();
print_struct(&bak_tag, &oreilly_rust);
print_visitor_example(&bak_tag, &oreilly_rust)
}
Cargo.toml
[package]
name = "design_pattern"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
derive_builder = "0.12.0"
print_struct関数の実行結果
Tags {
names: [
"rust",
"oreilly",
],
}
BookInfo {
name: "Programming Rust, 2nd Edition",
tags: Tags {
names: [
"rust",
"oreilly",
],
},
}
学習結果
- 型注釈によってポリモーフィズムを実現し、値を取得できました。
戻り値の型だけが違うので、ポリモーフィズムとは言えないかもしれません
fn print_visitor_example(tags:&Tags, bookinfo:&BookInfo){
let title:String = BookInfo::visit_title(&bookinfo);
let tag:String = BookInfo::visit_tags(&bookinfo.tags);
println!("{}",title);
println!("{}\n",tag);
let titles:Vec<String> = BookInfo::visit_title(&bookinfo);
let tags:Vec<String> = BookInfo::visit_tags(&bookinfo.tags);
println!("{:?}",titles);
println!("{:?}",tags);
}
print_visitor_example関数の実行結果
Programming Rust, 2nd Edition
rust, oreilly
["Programming Rust, 2nd Edition"]
["rust", "oreilly"]
その他
- Visitor解説ページ
Discussion
のコード例について- 変数名で使用されている略語の意味がわからなかったので調べました。
-
lhs
は左辺値 (left hand side) -
rhs
は右辺値 (right hand side)
-
- 変数名で使用されている略語の意味がわからなかったので調べました。
pub fn walk_expr(visitor: &mut Visitor, e: &Expr) {
match *e {
Expr::IntLit(_) => {},
Expr::Add(ref lhs, ref rhs) => {
visitor.visit_expr(lhs);
visitor.visit_expr(rhs);
}
Expr::Sub(ref lhs, ref rhs) => {
visitor.visit_expr(lhs);
visitor.visit_expr(rhs);
}
}
}
Discussion