前提
rustc 1.22.1
本題
traitを書く時、こういう書き方はできない。
trait Horizontal { // x:i64 というフィールドの存在を前提とする // x をカウントアップして、新しい値を返す fn count_up (&mut self) -> i64 { self.x += 1; self.x } } struct Point { x: i64, // コンパイラにフィールドの追加を強制される } impl Horizontal for Point {}
前提としてtraitのデフォルト実装はフィールドに干渉できない。
Traitを実装したStructに特定のフィールドを強制したい場合、 例えば、getter/setterの定義を用意することで実現できる。
trait Horizontal { fn get_x (&self) -> i64; fn set_x (&mut self, val: i64); fn count_up (&mut self) { let next = self.get_x() + 1; self.set_x( next ); self.get_x() } } struct Point { x: i64, // getter/setterのためにフィールドを用意する } impl Horizontal for Point { fn get_x (&self) -> i64 { self.x } fn set_x (&mut self, val: i64) { self.x = val; } }
余談: getter/setterのマクロを書いてみた
traitで使うaccessor
と、実装時に使うaccessor_impl
macro_rules! accessor { ((get=$getter:ident) : $type:ty ) => { fn $getter(&self) -> $type ; }; ((set=$setter:ident) : $type:ty ) => { fn $setter(&mut self, value:$type) ; }; ((get=$getter:ident, set=$setter:ident) : $type:ty ) => { accessor!((get=$getter): $type); accessor!((set=$setter): $type); }; } macro_rules! accessor_impl { ((get=$getter:ident) $name:ident : $type:ty ) => { fn $getter(&self) -> $type { self.$name } }; ((set=$setter:ident) $name:ident : $type:ty ) => { fn $setter(&mut self, value: $type) { self.$name = value; } }; ((get=$getter:ident, set=$setter:ident) $name:ident : $type:ty ) => { accessor_impl!((get=$getter) $name:$type); accessor_impl!((set=$setter) $name:$type); }; }
このマクロを使うとこんな感じになる。フィールドが増えないと効き目が薄い。
trait Horizontal { accessor!((get = get_x, set = set_x): i64); fn count_up(&mut self) -> i64 { let next = self.get_x() + 1; self.set_x(next); self.get_x() } } struct Point { x: i64, } impl Horizontal for Point { accessor_impl!((get = get_x, set = set_x) x: i64); }