Почему компилятор Rust не может определить подходящую функцию для вызова в общей специализации шаблона?

Рассмотрим следующий код:

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> соответственно? Есть ли что-то, чего я не понимаю в системе типов ржавчины?

  • русц версия 1.77.1

(Пожалуйста, простите меня за возможное неправильное использование терминологии ржавчины, я новичок в ржавчине, родом из C++)

Rust не поддерживает специализацию, и то, что вы делаете, — это не специализация, а два разных метода для разных типов, которые ни на чем не специализируются.

Chayim Friedman 06.04.2024 20:04

То есть вы хотите сказать, что если я создам Z<X> и Z<Y>, то это не специализация, а совершенно отдельный вид?

J.Horr 06.04.2024 20:06

Специализация — это если у вас есть общая реализация для Z<AnyT> и вы специализируете ее для Z<X> и Z<Y>. У вас просто есть реализация для них.

Chayim Friedman 06.04.2024 20:09

Да, Z<X> и Z<Y> — разные типы. Когда вы используете Z::new без указания параметра типа, компилятор делает все возможное, чтобы определить, каким должен быть этот параметр: если бы метод с таким именем существовал только для одного параметра типа, он бы использовал его, но когда существует несколько методов сопоставления, он этого не делает. Я не просматриваю их, чтобы определить, какие из них имеют удовлетворительную сигнатуру (вероятно, это хорошо, поскольку в противном случае небольшая программная ошибка может привести к очень неожиданным и трудно диагностируемым результатам), вместо этого, как вы видели, это просто ошибка неоднозначности.

eggyal 07.04.2024 09:19
Как создавать пользовательские общие типы в Python (50/100 дней Python)
Как создавать пользовательские общие типы в Python (50/100 дней Python)
Помимо встроенных типов, модуль типизации в Python предоставляет возможность определения общих типов, что позволяет вам определять типы, которые могут...
0
4
68
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Это работает, если вы обернете функцию 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;
}

Другие вопросы по теме