Rust 在特殊版本中调用函数的默认实现
Rust calling default implementation of function in specialized version
我在 Rust 中有一个特性,它为其函数提供了一些默认实现。
trait MyTrait {
fn do_something(&self);
fn say_hello(&self) {
println!("Hello I am default");
}
}
一些实现者扩展了这个特性并使用提供的默认值
struct MyNormalImplementor {}
impl MyTrait for MyNormalImplementor {
fn do_something(&self) {
// self.doing_some_normal_stuff();
}
}
现在我想要一个扩展特征行为的实现器,但有时仍然使用默认实现。当然默认实现比较复杂,我想遵循DRY原则。
struct MySpecializedImplementor(bool)
impl MyTrait for MySpecializedImplementor {
fn do_something(&self) {
// self.doing_some_wild_stuff();
}
fn say_hello(&self) {
if self.0 {
println!("hey, I am special");
} else {
MyTrait::say_hello(self);
}
}
}
此处MyTrait::say_hello(self);
立即无限循环调用专用函数。我没有找到任何方法来限定函数调用,以便调用 MyTrait
中的默认实现。有什么办法可以做到这一点,还是我必须为这种情况创建一个代理函数(也将在我的特征的 public 接口中)?
独立的通用函数
将默认实现推迟到独立的通用函数:
fn say_hello<T: Trait + ?Sized>(t: &T) {
println!("Hello I am default")
}
trait Trait {
fn say_hello(&self) {
say_hello(self);
}
}
struct Normal;
impl Trait for Normal {}
struct Special(bool);
impl Trait for Special {
fn say_hello(&self) {
if self.0 {
println!("Hey I am special")
} else {
say_hello(self)
}
}
}
fn main() {
let normal = Normal;
normal.say_hello(); // default
let special = Special(false);
special.say_hello(); // default
let special = Special(true);
special.say_hello(); // special
}
两个默认特征方法
另一种方法可以定义两个特征方法,一个作为默认实现,另一个遵循默认实现,除非它被覆盖:
trait Trait {
fn say_hello_default(&self) {
println!("Hello I am default");
}
fn say_hello(&self) {
self.say_hello_default();
}
}
struct Normal;
impl Trait for Normal {}
struct Special(bool);
impl Trait for Special {
fn say_hello(&self) {
if self.0 {
println!("Hey I am special");
} else {
self.say_hello_default();
}
}
}
fn main() {
let normal = Normal;
normal.say_hello(); // default
let special = Special(false);
special.say_hello(); // default
let special = Special(true);
special.say_hello(); // special
}
默认关联常量
虽然这有点笨拙,但如果默认和专用实现之间的差异减少到 const
值,那么您可以使用默认关联的 const
特征项作为特征:
trait Trait {
const MSG: &'static str = "Hello I am default";
fn say_hello(&self) {
println!("{}", Self::MSG);
}
}
struct Normal;
impl Trait for Normal {}
struct Special(bool);
impl Trait for Special {
const MSG: &'static str = "Hey I am special";
fn say_hello(&self) {
let msg = if self.0 {
Self::MSG
} else {
<Normal as Trait>::MSG
};
println!("{}", msg);
}
}
fn main() {
let normal = Normal;
normal.say_hello(); // default
let special = Special(false);
special.say_hello(); // default
let special = Special(true);
special.say_hello(); // special
}
通过 AsRef 调用默认实现
如果 Special
与 Normal
的唯一区别是一些额外的字段,并且 Special
类型可以用作 Normal
,那么您可能需要为 Special
实现 AsRef<Normal>
并以这种方式调用默认实现:
trait Trait {
fn say_hello(&self) {
println!("Hello I am default");
}
}
struct Normal;
impl Trait for Normal {}
struct Special(bool);
impl AsRef<Normal> for Special {
fn as_ref(&self) -> &Normal {
&Normal
}
}
impl Trait for Special {
fn say_hello(&self) {
if self.0 {
println!("Hey I am special");
} else {
<Normal as Trait>::say_hello(self.as_ref());
}
}
}
fn main() {
let normal = Normal;
normal.say_hello(); // default
let special = Special(false);
special.say_hello(); // default
let special = Special(true);
special.say_hello(); // special
}
默认宏实现
像往常一样,如果所有其他方法都失败了,让代码变干的最强力方法是使用宏:
macro_rules! default_hello {
() => {
println!("Hello I am default");
}
}
trait Trait {
fn say_hello(&self) {
default_hello!();
}
}
struct Normal;
impl Trait for Normal {}
struct Special(bool);
impl Trait for Special {
fn say_hello(&self) {
if self.0 {
println!("Hey I am special");
} else {
default_hello!();
}
}
}
fn main() {
let normal = Normal;
normal.say_hello(); // default
let special = Special(false);
special.say_hello(); // default
let special = Special(true);
special.say_hello(); // special
}
例如,syn::Visit
trait 具有类似的需求并执行此操作:对于每个 trait 方法,都有一个相应的独立函数,所有默认实现所做的就是调用相应的独立函数.如果 trait 实现需要做其他事情 and 委托给默认行为,它会做任何它需要做的事情并调用独立函数本身。
对于您的示例,它可能看起来像这样:
// default implementation
fn say_hello<T: ?Sized + MyTrait>(t: &T) {
println!("Hello I am default");
}
trait MyTrait {
fn do_something(&self);
fn say_hello(&self) {
// use default behavior
say_hello(self);
}
}
struct MySpecializedImplementor(bool)
impl MyTrait for MySpecializedImplementor {
fn do_something(&self) {
// self.doing_some_wild_stuff();
}
fn say_hello(&self) {
if self.0 {
println!("hey, I am special");
} else {
// use default behavior
say_hello(self);
}
}
}
我在 Rust 中有一个特性,它为其函数提供了一些默认实现。
trait MyTrait {
fn do_something(&self);
fn say_hello(&self) {
println!("Hello I am default");
}
}
一些实现者扩展了这个特性并使用提供的默认值
struct MyNormalImplementor {}
impl MyTrait for MyNormalImplementor {
fn do_something(&self) {
// self.doing_some_normal_stuff();
}
}
现在我想要一个扩展特征行为的实现器,但有时仍然使用默认实现。当然默认实现比较复杂,我想遵循DRY原则。
struct MySpecializedImplementor(bool)
impl MyTrait for MySpecializedImplementor {
fn do_something(&self) {
// self.doing_some_wild_stuff();
}
fn say_hello(&self) {
if self.0 {
println!("hey, I am special");
} else {
MyTrait::say_hello(self);
}
}
}
此处MyTrait::say_hello(self);
立即无限循环调用专用函数。我没有找到任何方法来限定函数调用,以便调用 MyTrait
中的默认实现。有什么办法可以做到这一点,还是我必须为这种情况创建一个代理函数(也将在我的特征的 public 接口中)?
独立的通用函数
将默认实现推迟到独立的通用函数:
fn say_hello<T: Trait + ?Sized>(t: &T) {
println!("Hello I am default")
}
trait Trait {
fn say_hello(&self) {
say_hello(self);
}
}
struct Normal;
impl Trait for Normal {}
struct Special(bool);
impl Trait for Special {
fn say_hello(&self) {
if self.0 {
println!("Hey I am special")
} else {
say_hello(self)
}
}
}
fn main() {
let normal = Normal;
normal.say_hello(); // default
let special = Special(false);
special.say_hello(); // default
let special = Special(true);
special.say_hello(); // special
}
两个默认特征方法
另一种方法可以定义两个特征方法,一个作为默认实现,另一个遵循默认实现,除非它被覆盖:
trait Trait {
fn say_hello_default(&self) {
println!("Hello I am default");
}
fn say_hello(&self) {
self.say_hello_default();
}
}
struct Normal;
impl Trait for Normal {}
struct Special(bool);
impl Trait for Special {
fn say_hello(&self) {
if self.0 {
println!("Hey I am special");
} else {
self.say_hello_default();
}
}
}
fn main() {
let normal = Normal;
normal.say_hello(); // default
let special = Special(false);
special.say_hello(); // default
let special = Special(true);
special.say_hello(); // special
}
默认关联常量
虽然这有点笨拙,但如果默认和专用实现之间的差异减少到 const
值,那么您可以使用默认关联的 const
特征项作为特征:
trait Trait {
const MSG: &'static str = "Hello I am default";
fn say_hello(&self) {
println!("{}", Self::MSG);
}
}
struct Normal;
impl Trait for Normal {}
struct Special(bool);
impl Trait for Special {
const MSG: &'static str = "Hey I am special";
fn say_hello(&self) {
let msg = if self.0 {
Self::MSG
} else {
<Normal as Trait>::MSG
};
println!("{}", msg);
}
}
fn main() {
let normal = Normal;
normal.say_hello(); // default
let special = Special(false);
special.say_hello(); // default
let special = Special(true);
special.say_hello(); // special
}
通过 AsRef 调用默认实现
如果 Special
与 Normal
的唯一区别是一些额外的字段,并且 Special
类型可以用作 Normal
,那么您可能需要为 Special
实现 AsRef<Normal>
并以这种方式调用默认实现:
trait Trait {
fn say_hello(&self) {
println!("Hello I am default");
}
}
struct Normal;
impl Trait for Normal {}
struct Special(bool);
impl AsRef<Normal> for Special {
fn as_ref(&self) -> &Normal {
&Normal
}
}
impl Trait for Special {
fn say_hello(&self) {
if self.0 {
println!("Hey I am special");
} else {
<Normal as Trait>::say_hello(self.as_ref());
}
}
}
fn main() {
let normal = Normal;
normal.say_hello(); // default
let special = Special(false);
special.say_hello(); // default
let special = Special(true);
special.say_hello(); // special
}
默认宏实现
像往常一样,如果所有其他方法都失败了,让代码变干的最强力方法是使用宏:
macro_rules! default_hello {
() => {
println!("Hello I am default");
}
}
trait Trait {
fn say_hello(&self) {
default_hello!();
}
}
struct Normal;
impl Trait for Normal {}
struct Special(bool);
impl Trait for Special {
fn say_hello(&self) {
if self.0 {
println!("Hey I am special");
} else {
default_hello!();
}
}
}
fn main() {
let normal = Normal;
normal.say_hello(); // default
let special = Special(false);
special.say_hello(); // default
let special = Special(true);
special.say_hello(); // special
}
例如,syn::Visit
trait 具有类似的需求并执行此操作:对于每个 trait 方法,都有一个相应的独立函数,所有默认实现所做的就是调用相应的独立函数.如果 trait 实现需要做其他事情 and 委托给默认行为,它会做任何它需要做的事情并调用独立函数本身。
对于您的示例,它可能看起来像这样:
// default implementation
fn say_hello<T: ?Sized + MyTrait>(t: &T) {
println!("Hello I am default");
}
trait MyTrait {
fn do_something(&self);
fn say_hello(&self) {
// use default behavior
say_hello(self);
}
}
struct MySpecializedImplementor(bool)
impl MyTrait for MySpecializedImplementor {
fn do_something(&self) {
// self.doing_some_wild_stuff();
}
fn say_hello(&self) {
if self.0 {
println!("hey, I am special");
} else {
// use default behavior
say_hello(self);
}
}
}