generics - 新类型作为 Rust 中的泛型参数?
问题描述
假设我有以下新类型:
pub struct Num(pub i32);
现在,我有一个接受可选的函数Num
:
pub fn calc(nu: Option<Num>) -> i32 {
let real_nu = match nu { // extract the value inside Num
Some(nu) => nu.0,
None => -1
};
// performs lots of complicated calculations...
real_nu * 1234
}
我想写的是一个extract
像下面这样的通用函数(不会编译):
// T here would be "Num" newtype
// R would be "i32", which is wrapped by "Num"
pub fn extract<T, R>(val: Option<T>) -> R {
match val {
Some(val) => val.0, // return inner number
None => -1 as R
}
}
这样我就可以绕过match
我的 calc 函数内部:
pub fn calc(nu: Option<Num>) -> i32 {
// do a lot of complicated calculations...
extract(nu) * 1234 // automatically extract i32 or -1
}
我该怎么写extract
?
动机:在我正在编写的程序中,有几个新类型,例如,并且Num
它们 wrapi8
和. 并且有许多不同的功能。在每个函数的开头编写所有这些 es变得非常重复。i16
i32
calc
match
calc
解决方案
这样的函数通常是不安全的,因为内部可能是私有的(因此访问受限)。例如,假设我们有一个新类型并Drop
为它实现。
struct NewType(String);
impl Drop for NewType {
fn drop(&mut self) {
println!("{}", self.0)
}
}
fn main() {
let x = NewType("abc".to_string());
let y = Some(x);
// this causes a compiler error
// let s = match y {
// Some(s) => s.0,
// None => panic!(),
// };
}
如果您的函数有效,您将能够将内部字符串移出新类型。然后当结构被删除时,它能够访问无效内存。
尽管如此,您可以编写一个宏来实现这些方面的某些东西。如果您尝试在实现Drop
的东西上使用宏,编译器会抱怨,否则,这应该可以工作。
macro_rules! extract_impl {
(struct $struct_name: ident($type_name: ty);) => {
struct $struct_name($type_name);
impl $struct_name {
fn extract(item: Option<Self>) -> $type_name {
match item {
Some(item) => item.0,
None => panic!(), // not sure what you want here
}
}
}
};
}
extract_impl! {
struct Num(i32);
}
impl Num {
fn other_fun(&self) {}
}
fn main() {
let x = Num(5);
println!("{}", Num::extract(Some(x)));
}
在宏的输出中包含一个impl
块不会导致任何问题,因为您可以根据impl
需要(在原始模块中)为单一类型拥有尽可能多的块。
更好的 API 是extract
返回一个选项,而不是一些无意义的值或恐慌。然后调用者可以轻松处理任何错误。
macro_rules! extract_impl {
(struct $struct_name: ident($type_name: ty);) => {
struct $struct_name($type_name);
impl $struct_name {
fn extract(item: Option<Self>) -> Option<$type_name> {
item.map(|item| item.0)
}
}
};
}
extract_impl! {
struct Num(i32);
}
impl Num {
fn other_fun(&self) {}
}
fn main() {
let x = Num(5);
println!("{:?}", Num::extract(Some(x)));
}
推荐阅读
- javascript - 我正在使用角度代码调用其余 api 并将其显示在屏幕上
- ios - 无法在 Xcode Storyboard 中调整 UITextField 的大小
- css - 当项目少于列时将 CSS 网格居中
- android - 如何在 Firebase-ui ListVeiw 中实现点击监听器?
- rest - 如何在 QTP 中添加 Oauth Bearer 令牌
- php - 使用 jquery 将图像上传到数据库时,我在 php 中遇到错误,变量中的数据可能没有传递
- corda - 关于corda中附件的详细信息
- linux - 如何设计对 nanomsg 套接字和 tty 或 Netlink 的异步处理?
- c# - 刷新 DataSource Master-Detail XtraGrid
- python - nargs='?' 当两个 pos 参数被一个 opt 参数分割时不起作用,为什么?