有没有办法提示编译器在使用 Option::None 时使用某种默认泛型类型?
Is there a way to hint to the compiler to use some kind of default generic type when using Option::None?
我需要一个函数来获取实现特征 std::iter::IntoIterator
的通用类型 T
的 Option
。
一个天真的实现可能如下所示(是的,解包会在 None
上出现恐慌):
fn main() {
let v = vec![1i32, 2, 3];
print_iter(Some(v));
print_iter(None);
}
fn print_iter<T: IntoIterator<Item = i32>>(v: Option<T>) {
for e in v.unwrap() {
println!("{}", e);
}
}
测试playground。
这对 Some(...)
按预期工作,但对 None
失败:
error[E0282]: type annotations needed
--> src/main.rs:4:5
|
4 | print_iter(None);
| ^^^^^^^^^^ cannot infer type for `T`
显然在那些情况下 T
的类型是未知的。可以使用 print_iter::<Vec<i32>>(None);
但这感觉并不是真正的惯用,因为它给出了一些不基于任何东西的任意类型...
有什么方法可以提示编译器我不关心 None
或使用某种默认值吗?
值None
没有问题。
当无法推断您的变量类型时,这只是一个问题,这通常不会发生。唯一有问题的情况是当您直接传递 None
并且没有类型化变量时。
您可以使用涡轮鱼指定类型:
print_iter::<Vec<i32>>(None);
但您通常不需要;你的正常情况宁愿是这样的:
let a: Option<Vec<i32>> = None;
print_iter(a);
或
print_iter(my_options.a);
并且两个构造都没有问题。
现在(在问题编辑之后),如果你真的希望能够通过 None
而不精确化类型,例如作为文字标志,那么你可以定义一个宏:
macro_rules! pi {
(None) => {
// here we handle the call as a nop but
// other behaviors are possible, like
// calling the function with a type
// specified with a turbofish
};
($a:expr) => {
print_iter($a)
};
}
fn main() {
let v = vec![1i32, 2, 3];
pi!(Some(v));
pi!(None);
}
fn print_iter<T: IntoIterator<Item = i32>>(v: Option<T>) {
for e in v.unwrap() {
println!("{}", e);
}
}
Is there any way to hint to the compiler that I don't care for None
or use some kind of default?
您可以实现自己的非通用值作为默认值。对于初学者,我们假设 print_iter
不接受 Option<T>
,而是接受它自己的枚举:
enum PrintArg<T> {
Ignore,
Use(T),
}
fn print_iter<T: IntoIterator<Item = i32>>(v: PrintArg<T>) {
if let PrintArg::Use(v) = v {
for e in v {
println!("{}", e);
}
}
}
这还没有解决问题,因为如果你将 PrintArg::Ignore
传递给 print_iter()
,你又回到了原点——编译器无法推断出 T
.但是使用您自己的类型,您可以轻松更改 print_iter
以接受任何可以 converted into PrintArg
:
fn print_iter<T, V>(v: T)
where
T: Into<PrintArg<V>>,
V: IntoIterator<Item = i32>,
{
if let PrintArg::Use(v) = v.into() {
for e in v {
println!("{}", e);
}
}
}
通过此修改,您可以创建一个虚拟的非泛型 Ignore
值并使用 From
特征定义其到 PrintArg::Ignore<T>
的 T
的转换您的选择 - 例如:
struct Ignore;
impl From<Ignore> for PrintArg<Vec<i32>> {
fn from(_v: Ignore) -> Self {
PrintArg::Ignore
}
}
由于 Ignore
是非通用的,因此它的使用不需要(或接受)<T>
。虽然我们确实必须在 From
特征实现中为 PrintArg<T>
发明一种类型,但我们从未构造它,所以我们选择哪个类型无关紧要,只要它满足 IntoIterator
界限。
当然,您仍然希望能够使用 Some(...)
调用 print_iter()
,因此您还将定义 Option<T>
到 PrintArg<T>
的转换:
impl<T> From<Option<T>> for PrintArg<T> {
fn from(v: Option<T>) -> Self {
match v {
Some(v) => PrintArg::Use(v),
None => PrintArg::Ignore,
}
}
}
有了这些,你的 API 就干净了,让 main()
看起来像这样 (playground):
fn main() {
let v = vec![1i32, 2, 3];
print_iter(Some(v));
print_iter(Ignore);
}
我需要一个函数来获取实现特征 std::iter::IntoIterator
的通用类型 T
的 Option
。
一个天真的实现可能如下所示(是的,解包会在 None
上出现恐慌):
fn main() {
let v = vec![1i32, 2, 3];
print_iter(Some(v));
print_iter(None);
}
fn print_iter<T: IntoIterator<Item = i32>>(v: Option<T>) {
for e in v.unwrap() {
println!("{}", e);
}
}
测试playground。
这对 Some(...)
按预期工作,但对 None
失败:
error[E0282]: type annotations needed
--> src/main.rs:4:5
|
4 | print_iter(None);
| ^^^^^^^^^^ cannot infer type for `T`
显然在那些情况下 T
的类型是未知的。可以使用 print_iter::<Vec<i32>>(None);
但这感觉并不是真正的惯用,因为它给出了一些不基于任何东西的任意类型...
有什么方法可以提示编译器我不关心 None
或使用某种默认值吗?
值None
没有问题。
当无法推断您的变量类型时,这只是一个问题,这通常不会发生。唯一有问题的情况是当您直接传递 None
并且没有类型化变量时。
您可以使用涡轮鱼指定类型:
print_iter::<Vec<i32>>(None);
但您通常不需要;你的正常情况宁愿是这样的:
let a: Option<Vec<i32>> = None;
print_iter(a);
或
print_iter(my_options.a);
并且两个构造都没有问题。
现在(在问题编辑之后),如果你真的希望能够通过 None
而不精确化类型,例如作为文字标志,那么你可以定义一个宏:
macro_rules! pi {
(None) => {
// here we handle the call as a nop but
// other behaviors are possible, like
// calling the function with a type
// specified with a turbofish
};
($a:expr) => {
print_iter($a)
};
}
fn main() {
let v = vec![1i32, 2, 3];
pi!(Some(v));
pi!(None);
}
fn print_iter<T: IntoIterator<Item = i32>>(v: Option<T>) {
for e in v.unwrap() {
println!("{}", e);
}
}
Is there any way to hint to the compiler that I don't care for
None
or use some kind of default?
您可以实现自己的非通用值作为默认值。对于初学者,我们假设 print_iter
不接受 Option<T>
,而是接受它自己的枚举:
enum PrintArg<T> {
Ignore,
Use(T),
}
fn print_iter<T: IntoIterator<Item = i32>>(v: PrintArg<T>) {
if let PrintArg::Use(v) = v {
for e in v {
println!("{}", e);
}
}
}
这还没有解决问题,因为如果你将 PrintArg::Ignore
传递给 print_iter()
,你又回到了原点——编译器无法推断出 T
.但是使用您自己的类型,您可以轻松更改 print_iter
以接受任何可以 converted into PrintArg
:
fn print_iter<T, V>(v: T)
where
T: Into<PrintArg<V>>,
V: IntoIterator<Item = i32>,
{
if let PrintArg::Use(v) = v.into() {
for e in v {
println!("{}", e);
}
}
}
通过此修改,您可以创建一个虚拟的非泛型 Ignore
值并使用 From
特征定义其到 PrintArg::Ignore<T>
的 T
的转换您的选择 - 例如:
struct Ignore;
impl From<Ignore> for PrintArg<Vec<i32>> {
fn from(_v: Ignore) -> Self {
PrintArg::Ignore
}
}
由于 Ignore
是非通用的,因此它的使用不需要(或接受)<T>
。虽然我们确实必须在 From
特征实现中为 PrintArg<T>
发明一种类型,但我们从未构造它,所以我们选择哪个类型无关紧要,只要它满足 IntoIterator
界限。
当然,您仍然希望能够使用 Some(...)
调用 print_iter()
,因此您还将定义 Option<T>
到 PrintArg<T>
的转换:
impl<T> From<Option<T>> for PrintArg<T> {
fn from(v: Option<T>) -> Self {
match v {
Some(v) => PrintArg::Use(v),
None => PrintArg::Ignore,
}
}
}
有了这些,你的 API 就干净了,让 main()
看起来像这样 (playground):
fn main() {
let v = vec![1i32, 2, 3];
print_iter(Some(v));
print_iter(Ignore);
}