尽管Rust吸收了许多好的现代编程思想,但似乎并没有提供一项非常基本的功能。
现代(伪)功能代码基于以下种类的大量类:
pub struct NamedTuple {
a: i8,
b: char,
}
impl NamedTuple {
fn new(a: i8, b: char) -> NamedTuple {
NamedTuple { a: a, b: b }
}
fn a(&self) -> i8 {
self.a
}
fn b(&self) -> char {
self.b
}
}
如您所见,这里有很多样板代码。没有样板代码,真的没有办法紧凑地描述这些类型吗?
答案 0 :(得分:11)
当您有了样板时,请考虑宏:
macro_rules! ro {
(
pub struct $name:ident {
$($fname:ident : $ftype:ty),*
}
) => {
pub struct $name {
$($fname : $ftype),*
}
impl $name {
fn new($($fname : $ftype),*) -> $name {
$name { $($fname),* }
}
$(fn $fname(&self) -> $ftype {
self.$fname
})*
}
}
}
ro!(pub struct NamedTuple {
a: i8,
b: char
});
fn main() {
let n = NamedTuple::new(42, 'c');
println!("{}", n.a());
println!("{}", n.b());
}
这是一个基本的宏,可以扩展以处理指定的可见性以及结构和字段上的属性/文档。
我要挑战的是,您拥有与您想象的一样多的样板。例如,您仅显示Copy
类型。在结构中添加String
或Vec
后,它就会崩溃,您需要返回引用或获取self
。
从编辑上来说,我认为这不是好的或惯用的Rust代码。如果您有需要人们挖掘的值类型,请仅将字段设为公开:
pub struct NamedTuple {
pub a: i8,
pub b: char,
}
fn main() {
let n = NamedTuple { a: 42, b: 'c' };
println!("{}", n.a);
println!("{}", n.b);
}
现有的Rust功能可防止吸气剂方法试图解决的大多数问题。
基于绑定的可变性
n.a = 43;
error[E0594]: cannot assign to field `n.a` of immutable binding
引用规则
struct Something;
impl Something {
fn value(&self) -> &NamedTuple { /* ... */ }
}
fn main() {
let s = Something;
let n = s.value();
n.a = 43;
}
error[E0594]: cannot assign to field `n.a` of immutable binding
如果您已将值类型的所有权转让给其他人,谁在乎,如果他们更改了它?
请注意,我正在按照Growing Object-Oriented Software Guided by Tests描述的值类型进行区分,它们与对象有所区别。对象不应暴露内部。
答案 1 :(得分:5)
Rust没有提供生成吸气剂的内置方法。但是,有多个Rust功能可以用来处理样板代码!您的问题中最重要的两个:
#[derive(...)]
属性macro_rules!
的宏(有关如何使用它们来解决问题的信息,请参见@Shepmaster's answer)我认为避免这种样板代码的最佳方法是使用自定义派生。这使您可以为类型添加#[derive(...)]
属性,并在编译时生成这些getter。
已经有一个提供以下功能的板条箱: derive-getters
。它是这样的:
#[derive(Getters)]
pub struct NamedTuple {
a: i8,
b: char,
}
也有getset
,但有两个问题:getset
的板条箱名称应为derive
,但更重要的是,它鼓励“为所有内容提供者和设置者”通过提供模式也可以生成不执行任何检查的设置器。
最后,您可能需要考虑重新考虑Rust中的编程方法。老实说,根据我的经验,“获取样板”几乎不是问题。当然,有时您需要编写吸气剂,而不是“大量”的吸气剂。
在Rust中,可变性也不是唯一的。 Rust是一种多范式语言,支持多种编程风格。成语的Rust在每种情况下都使用最有用的范例。完全避免突变可能不是在Rust中编程的最佳方法。此外,避免可变性不仅是通过为您的字段提供getter来实现的,绑定和引用可变性就更为重要!
因此,对有用的字段使用只读访问权限,但不适用于所有字段。