Рассмотрим следующий код:
trait A {}
struct X { p1: i32 }
struct Y { p1: i32, p2: i32 }
impl A for X {}
impl A for Y {}
struct Z<T> where T: A {
data: T
}
impl Z<X> {
fn new(p1: i32) -> Self {
Self { data: X { p1: p1 } }
}
}
impl Z<Y> {
fn new(p1: i32, p2: i32) -> Self {
Self { data: Y { p1: p1, p2: p2 } }
}
}
При совместном создании Z<X>
и Z<Y>
вот так:
let zx: Z<X> = Z::new(1);
let zy: Z<Y> = Z::new(2, 3);
компилятор выдает ошибку, предположительно говоря, что Z::new()
существует дважды:
error[E0034]: multiple applicable items in scope
--> main.rs:33:23
|
33 | let zx: Z<X> = Z::new(1);
| ^^^ multiple `new` found
|
Однако при создании Z<X>
и Z<Y>
вот так:
let zx: Z<X> = Z::<X>::new(1);
let zy: Z<Y> = Z::<Y>::new(2, 3);
все компилируется нормально.
Теперь мне интересно:
Зачем вручную указывать использование <X>
и <Y>
, если эта информация уже должна быть известна с учетом аннотации типа?
И вдогонку вопрос:
Почему кажется, что new()
существует дважды в Z, но только один раз в Z<X>
и Z<Y>
соответственно?
Есть ли что-то, чего я не понимаю в системе типов ржавчины?
(Пожалуйста, простите меня за возможное неправильное использование терминологии ржавчины, я новичок в ржавчине, родом из C++)
То есть вы хотите сказать, что если я создам Z<X>
и Z<Y>
, то это не специализация, а совершенно отдельный вид?
Специализация — это если у вас есть общая реализация для Z<AnyT>
и вы специализируете ее для Z<X>
и Z<Y>
. У вас просто есть реализация для них.
Да, Z<X>
и Z<Y>
— разные типы. Когда вы используете Z::new
без указания параметра типа, компилятор делает все возможное, чтобы определить, каким должен быть этот параметр: если бы метод с таким именем существовал только для одного параметра типа, он бы использовал его, но когда существует несколько методов сопоставления, он этого не делает. Я не просматриваю их, чтобы определить, какие из них имеют удовлетворительную сигнатуру (вероятно, это хорошо, поскольку в противном случае небольшая программная ошибка может привести к очень неожиданным и трудно диагностируемым результатам), вместо этого, как вы видели, это просто ошибка неоднозначности.
Это работает, если вы обернете функцию new
и тип ее параметра в фабричный признак. Хотя для этого потребуется один параметр конструктора, требующий кортежа вместо нескольких аргументов:
trait A {}
struct X {
p1: i32,
}
struct Y {
p1: i32,
p2: i32,
}
impl A for X {}
impl A for Y {}
struct Z<T>
where
T: A,
{
data: T,
}
trait ZFactory {
type Args;
fn new(args: Self::Args) -> Self;
}
impl ZFactory for Z<X> {
type Args = i32;
fn new(p1: i32) -> Self {
Self { data: X { p1 } }
}
}
impl ZFactory for Z<Y> {
type Args = (i32, i32);
fn new(args: Self::Args) -> Self {
Self {
data: Y {
p1: args.0,
p2: args.1,
},
}
}
}
fn main() {
let zx: Z<X> = Z::new(1);
let zy: Z<Y> = Z::new((2, 3));
let zxa: &dyn A = &zx.data;
let zxb: &dyn A = &zy.data;
}
Rust не поддерживает специализацию, и то, что вы делаете, — это не специализация, а два разных метода для разных типов, которые ни на чем не специализируются.