Rust 泛型实现块使泛型类型的实现方法变得更加容易。
在本文中,我们将通过首先了解没有泛型的世界的乏味以及使用它们时的结果来说明泛型实现块的价值。
要在本文中继续前进:
-
Rust 结构和 impl块
-
通用类型
-
通用实现块
-
用通用实现火车 impl块
Rust 结构和 impl块
与 C++、JavaScript 和 C# 等语言不同,在 Rust 中,您没有用于定义对象类型的对象(属性和方法组)或类。
相反, Rust 和 Go 一样 ,选择只是属性组的结构。 在 Rust 中,通过使用实现将方法添加到结构中,并且 impl块用于定义方法。 请参阅下面的示例。
// Struct Definition struct Person { name: String } // Impl block defining method impl Person { fn say_name(&self){ println!("{}", self.name); } } fn main() { // Instantiate a person let alex = Person {name: "Alex Merced".to_string()}; // Call methods alex.say_name(); }
在上面的代码中,我们创建了一个 Person结构与一个 String属性称为 name. 然后,我们使用了一个 impl块来定义和实现一个 say_name方法。 在主函数中,我们创建了一个 Person结构并调用 say_name()方法,它将打印 Alex Merced.
通用类型
有时,我们的结构可能具有类型定义不同的属性。 为了指定这一点,我们可以将未知类型列为变量,例如 <T>或者 <T, U>,取决于我们需要指定多少变量类型。
然后,我们需要为我们想要支持的每种类型实现任何方法。 如果我们要为多种类型实现一个方法,这可能会非常乏味。
// Generic Struct Definition, T is a variable for unknown type
struct Stats<T> {
age: T,
height:T
}
// Impl blocks defining methods for different types
impl Stats<i32> {
fn print_stats(&self){
println!("Age is {} years and Height is {} inches", self.age, self.height);
}
}
impl Stats<f32> {
fn print_stats(&self){
println!("Age is {} years and Height is {} feet", self.age, self.height);
}
}
fn main() {
// Instantiate using i32 stats
let alex = Stats {age: 37, height: 70};
// Instantiate using f32 stats
let alex2 = Stats {age: 37.0, height: 5.83};
// Call methods
alex.print_stats();
alex2.print_stats();
}
在上面的示例代码中,我们定义了一个 Statsstruct,它有两个相同类型的属性,记为 T. 因为我们在写的时候把它定义为泛型类型 <T>,这种类型可以是任何东西,但年龄和身高必须相同。
我们想要任何 Stats使用两个 32 位数字(无论是整数还是浮点数)的对象 print_stats方法。 在这种情况下,我们必须为每种可能性创建两个实现块。
超过 20 万开发人员使用 LogRocket 来创造更好的数字体验 了解更多 →
然后我们实例化两个 Stats使用的结构 i32价值观和 f32价值观。 我们称 print_stats结构获取以下输出的方法:
Age is 37 years and Height is 70 inches Age is 37 years and Height is 5.83 feet
注意不同的输出,因为我们从正确的实现块调用了实现。
通用实现块
为每种类型编写单独的实现块可能会很乏味,尤其是在有多种可能性的情况下。
如果几种类型的实现是相同的,我们可以使用泛型实现块,它与泛型类型一样,允许我们定义表示类型的变量:
use std::fmt::Display; // Generic Struct Definition, T is a variable for unknown type struct Stats<T> { age: T, height:T } // Impl blocks defining methods for different types impl<T:Display> Stats<T> { fn print_stats(&self){ println!("Age is {} years and Height is {}", self.age, self.height); } } fn main() { // Instantiate using i32 stats let alex = Stats {age: 37, height: 70}; // Instantiate using f32 stats let alex2 = Stats {age: 37.0, height: 5.83}; // Instantiate using String stats let alex3 = Stats {age: "37".to_string(), height: "5'10ft".to_string()}; // Call methods alex.print_stats(); alex2.print_stats(); alex3.print_stats(); }
在上面的代码中,我们导入了 Display来自标准库的 trait,因为我们稍后需要引用它。
我们定义了 Statsstruct 作为具有相同泛型类型、age 和 height 的两个属性的结构。
我们只使用了一个实现块来定义一个泛型类型 T:Display, 意思是 T是泛型类型的变量。 :Display意味着它必须是实现 Display特征。 这是必需的,因此我们可以使用 println!此实现中的宏。
然后我们定义了三个结构:一个使用 i32,另一个使用 f32,最后一个使用字符串作为其属性值。 (我们使用了三种不同的类型,只需要一种 impl堵塞。 多么酷啊!)
当它运行时,您应该得到以下输出:
Age is 37 years and Height is 70 Age is 37 years and Height is 5.83 Age is 37 years and Height is 5'10ft
用通用实现火车 impl块
通用的 impl当我们需要在许多类型上实现特征时,块也很有用。 在这种情况下,我们可以使用通用的实现来节省时间,然后根据需要定义具体的实现。 您可以在下面的代码中看到这一点。
use std::fmt::Display; // Generic Struct Definition, T is a variable for unknown type struct Stats<T> { age: T, height:T } // Generic Tuple Struct that holds one value struct Number<T> (T); // trait with default implementation of print_stats method trait ViewStats { fn print_stats(&self){ println!("The type of age and height doesn't implement the display trait") } } // Impl blocks defining methods for different types impl<T:Display> ViewStats for Stats<T> { fn print_stats(&self){ println!("Age is {} years and Height is {}", self.age, self.height); } } //Impl block to ViewStats trait to number but use default implementation impl<T> ViewStats for Stats<Number<T>> {} fn main() { // Instantiate using i32 stats let alex = Stats {age: 37, height: 70}; // Instantiate using f32 stats let alex2 = Stats {age: 37.0, height: 5.83}; // Instantiate using String stats let alex3 = Stats {age: "37".to_string(), height: "5'10ft".to_string()}; // Instantiate using String stats let alex4 = Stats {age: Number(37), height: Number(70)}; // Call methods alex.print_stats(); alex2.print_stats(); alex3.print_stats(); alex4.print_stats(); }
第二空间App,私密视频图片隐藏软件,老司机的秘密空间
在上面的代码中,我们导入了 Display来自标准库的特征。 我们定义了 Statsstruct 作为具有以下属性的结构 age和 height任何类型,匹配类型除外。
来自 LogRocket 的更多精彩文章:
-
不要错过 The Replay 来自 LogRocket 的精选时事通讯
-
使用 React 的 useEffect 优化应用程序的性能
-
之间切换 在多个 Node 版本
-
了解如何 使用 AnimXYZ 为您的 React 应用程序制作动画
-
探索 Tauri ,一个用于构建二进制文件的新框架
-
比较 NestJS 与 Express.js
-
发现 TypeScript 领域中使用的流行 ORM
然后,我们定义了一个 Numberstruct,它是一个具有一个值的元组。 这只是为了演示当我们使用不实现显示的类型创建统计信息时会发生什么。
接下来,我们定义了一个 ViewStats特质在哪里 print_stats方法被赋予了默认实现。 如果值为 age和 height是有效类型,但没有自己的实现。
然后,我们定义了一个实现 ViewStats为了 Stats<T>, 在哪里 T是任何实现 Display特质,比如 i32, f32, 和 String.
然后,我们实现了 ViewStats为了 Number. 因为我们没有定义新版本 print_stats,它将使用特征声明块中的默认值。
然后我们创建了四个结构。 前三个和以前一样,第四个是 Stats年龄和身高表示为的结构 Number结构。
当我们运行脚本时,我们得到以下输出:
Age is 37 years and Height is 70 Age is 37 years and Height is 5.83 Age is 37 years and Height is 5'10ft The type of age and height doesn't implement the display trait
请注意,最后一行显示了我们在训练块中的默认实现的结果,因为 Number<T>不实现显示,所以它不能使用与前三个实例相同的实现 Stats<T>.
结论
泛型简化了应与多种类型一起使用的代码的编写,无论是使用泛型定义属性类型还是泛型 impl块来定义方法,而无需重复这样做的乏味。
LogRocket :全面了解生产 Rust 应用程序
调试 Rust 应用程序可能很困难,尤其是当用户遇到难以重现的问题时。 如果您对监控和跟踪 Rust 应用程序的性能、自动显示错误以及跟踪缓慢的网络请求和加载时间感兴趣,请 尝试 LogRocket 。
LogRocket 就像一个用于 Web 和移动应用程序的 DVR,几乎可以记录 Rust 应用程序上发生的所有事情。 无需猜测问题发生的原因,您可以汇总并报告问题发生时应用程序所处的状态。 LogRocket 还监控您的应用程序的性能,报告客户端 CPU 负载、客户端内存使用情况等指标。