泛型结构的"new()"函数不接受泛型结构字段的非泛型赋值



前几天,当我开始学习泛型如何在 rust 上工作时,我遇到了一个问题。我遇到的问题在以下代码片段中:

struct Example<T> {
a: T
}
impl<T> Example<T> {
fn new() -> Self {
Self {
a: 5
}
}
}

尝试编译它会给出以下输出:

error[E0308]: mismatched types
--> src/lib.rs:11:16
|
8  | impl<T> Example<T> {
|      - this type parameter
...
11 |             a: 5
|                ^ expected type parameter `T`, found integer
|
= note: expected type parameter `T`
found type `{integer}`
For more information about this error, try `rustc --explain E0308`.
warning: `url_shortener` (lib) generated 2 warnings
error: could not compile `url_shortener` due to previous error; 2 warnings emitted

一个简单的解决方法是指定我们想要的类型,如下所示:

impl Example<i32> {
fn new() -> Self {
Self {
a: 5
}
}
}

但我真的不明白为什么保持类型参数通用不起作用。我还有一个更改类型参数不那么容易的例子。假设我们想做如下事情:

use itertools::Itertools;

struct Example<T: Iterator<Item=String>> {
a: T
}

fn get_permutations(n: i32) -> impl Iterator<Item=String> {
(0..n)
.map(|_| (97u8..=122u8).map(|x| x as char))
.multi_cartesian_product().map(|v| v.iter().join(""))
}
impl<T: Iterator<Item=String>> Example<T> {
fn new() -> Self {
let test = get_permutations(4)
.chain(
get_permutations(3)
)
.chain(
get_permutations(2)
)
.chain(
get_permutations(1)
);
Self {
a: test
}
}
}

test的类型是Chain<Chain<Chain<impl Iterator<Item = String>, impl Iterator<Item = String>>, impl Iterator<Item = String>>, impl Iterator<Item = String>>,我们不能简单地将泛型类型参数T更改为该参数,因为类型参数不允许使用impl Trait语法。

我们可以尝试更改get_permutations函数的返回类型,使其不返回impl Trait,但get_permutations返回的实际类型包括闭包,我不知道如何写出来(或者如果甚至有可能考虑到每个闭包都是它自己的类型(?此外,我们可能希望在以后将更多功能链接在一起,并且每次我们想要这样做时更改其类型都是不好的。

因此,似乎最好get_permutations返回类型作为一些通用迭代器,我能想到的唯一解决方案是像这样使用 Boxes:

use itertools::Itertools;

struct Example {
a: Box<dyn Iterator<Item = String>>
}

fn get_permutations(n: i32) -> Box<dyn Iterator<Item = String>> {
(0..n)
.map(|_| (97u8..=122u8).map(|x| x as char))
.multi_cartesian_product().map(|v| v.iter().join(""))
}
impl Example {
fn new() -> Self {
let test = Box::new(get_permutations(4)
.chain(
get_permutations(3)
)
.chain(
get_permutations(2)
)
.chain(
get_permutations(1)
));
Self {
a: test
}
}
}

但我觉得这种丑陋。我也可以在不使用new()的情况下创建对象:

use itertools::Itertools;

struct Example<T: Iterator<Item=String>> {
a: T
}

fn get_permutations(n: i32) -> impl Iterator<Item=String> {
(0..n)
.map(|_| (97u8..=122u8).map(|x| x as char))
.multi_cartesian_product().map(|v| v.iter().join(""))
}
fn main(){
let test = Example{a: get_permutations(4)
.chain(
get_permutations(3)
)
.chain(
get_permutations(2)
)
.chain(
get_permutations(1)
)};

for perm in test.a {
println!("{perm}")
}
}

但是,如果每个Example对象都应该包含这个精确的迭代器,这将没有多大意义。那么,有没有更好的方法可以在不使用 Box 的情况下做到这一点?

您的简单示例不起作用,因为泛型是由用户指定的,即Example::<String>::new()对您的实现没有意义,因为它会尝试将5分配给String

对于您的实际用例,您希望能够存储您不想命名的类型,并希望避免使用特征对象。截至目前,这是不可能的 AFAIK。有一个不稳定的功能,#![feature(type_alias_impl_trait)],这将是理想的,看起来像这样:

#![feature(type_alias_impl_trait)] // <------------
use itertools::Itertools;
type Permutations = impl Iterator<Item = String>; // <------------
struct Example {
a: Permutations,
}
fn get_permutations(n: i32) -> impl Iterator<Item = String> {
(0..n)
.map(|_| (97u8..=122u8).map(|x| x as char))
.multi_cartesian_product()
.map(|v| v.iter().join(""))
}
impl Example {
fn new() -> Self {
let test = get_permutations(4)
.chain(get_permutations(3))
.chain(get_permutations(2))
.chain(get_permutations(1));
Self { a: test }
}
}

您的稳定选择是使用 trait 对象,Box<dyn Iterator<Item = String>>,正如您已经发现的那样,或者您可以保留一个泛型参数并让函数通过Example<impl Iterator<Item = String>>返回具体但不透明的类型:

use itertools::Itertools;
struct Example<T: Iterator<Item = String>> {
a: T,
}
fn get_permutations(n: i32) -> impl Iterator<Item = String> {
(0..n)
.map(|_| (97u8..=122u8).map(|x| x as char))
.multi_cartesian_product()
.map(|v| v.iter().join(""))
}
impl<T: Iterator<Item = String>> Example<T> {
fn new() -> Example<impl Iterator<Item = String>> { // <------------
let test = get_permutations(4)
.chain(get_permutations(3))
.chain(get_permutations(2))
.chain(get_permutations(1));
Example { a: test }
}
}

两者都有其缺点,因为前者需要分配和动态调度,但后一种方法仍然是不可命名的类型(不能在结构中存储Example<impl ...>),并且仍然可以被用户覆盖。

最新更新