2017-02-14 7 views
1

Я пытаюсь создать конструктор new с одним и двумя аргументами, но я не могу понять, как это сделать. Возможно ли это на данный момент?Можно ли перегрузить функцию с различными числами аргументов (используя черты)

То, что я теперь дает мне ошибку, что несколько применимые элементы находятся в области видимости (playground)

trait __Constructor1<T> { 
    fn new(T) -> Self; 
} 
trait __Constructor2<T, U> { 
    fn new(T, U) -> Self; 
} 

enum MixedInts { 
    SmallInt(i32), 
    TwoSmallInts(i32, i32), 
} 

impl __Constructor1<i32> for MixedInts { 
    fn new(__0: i32) -> MixedInts { 
     MixedInts::SmallInt(__0) 
    } 
} 
impl __Constructor2<i32, i32> for MixedInts { 
    fn new(__0: i32, __1: i32) -> MixedInts { 
     MixedInts::TwoSmallInts(__0, __1) 
    } 
} 

fn main() { 
    let x = MixedInts::new(2i32); 
    let y = MixedInts::new(2i32, 2i32); 
} 
+1

Я знаю, что вы только что пытались создать небольшой пример .. но использование черта здесь довольно бесполезно. Немного повернув ваш пример на голове, я [видел эту технику, используемую в нескольких кодах) (https://play.rust-lang.org/?gist=2ae7e995af81de014bd8a59971021649&version=stable&backtrace=0). –

+0

@SimonWhitehead Я не уверен, что ваш код должен достичь. Он не принимает другое количество аргументов вообще и по какой-то причине создает вместо второго перечисления второй тип. – JelteF

+0

Цель состоит в том, чтобы продемонстрировать _use enum_ как способ «перегрузить» функцию. Вы можете использовать вариант перечисления для передачи в функцию различных чисел аргументов. Таким образом, он переворачивает ваш пример с его помощью, используя enum как механизм для «перегрузки». Я просто использовал структуру в качестве примера, чтобы подчеркнуть, что перечисление в моем примере является основной частью. –

ответ

7

Это технически возможно, но не в способе, которым это практично. Вы должны использовать Rust's Universal Function Call Syntax, чтобы он мог устранить вызовы new.

fn main() { 
    let x = <MixedInts as __Constructor1<i32>>::new(2i32); 
    let y = <MixedInts as __Constructor2<i32, i32>>::new(2i32, 2i32); 
} 

Рамки Железо имеет интересный Modifier pattern, что я думаю, что выполняет то, что вы хотите. Хотя это довольно умно, это в конечном счете запутывает пользователя.

4

Rust не поддерживает перегруженные функции/методы. В качестве обходного пути вы можете использовать кортежи для получения нескольких значений в одном аргументе. Затем вы можете определить признак и реализовать его для допустимых типов этого единственного аргумента, и функция просто делегирует реализацию реализации.

enum MixedInts { 
    SmallInt(i32), 
    TwoSmallInts(i32, i32), 
} 

trait IntoMixedInts { 
    fn into(self) -> MixedInts; 
} 

impl MixedInts { 
    fn new<A>(args: A) -> MixedInts 
     where A: IntoMixedInts 
    { 
     args.into() 
    } 
} 

impl IntoMixedInts for i32 { 
    fn into(self) -> MixedInts { 
     MixedInts::SmallInt(self) 
    } 
} 

impl IntoMixedInts for (i32, i32) { 
    fn into(self) -> MixedInts { 
     MixedInts::TwoSmallInts(self.0, self.1) 
    } 
} 

fn main() { 
    let x = MixedInts::new(2i32); 
    let y = MixedInts::new((2i32, 2i32)); 
} 

Примечание: В этом примере, вы можете использовать стандартную From и Into черту вместо определения собственного признака. Однако это может не работать для других свойств, из-за правил согласованности (правила, которые гарантируют, что может существовать только одна реализация определенного признака для определенного типа).

enum MixedInts { 
    SmallInt(i32), 
    TwoSmallInts(i32, i32), 
} 

impl MixedInts { 
    fn new<A>(args: A) -> MixedInts 
     where A: Into<MixedInts> 
    { 
     args.into() 
    } 
} 

impl From<i32> for MixedInts { 
    fn from(a: i32) -> MixedInts { 
     MixedInts::SmallInt(a) 
    } 
} 

impl From<(i32, i32)> for MixedInts { 
    fn from((a, b): (i32, i32)) -> MixedInts { 
     MixedInts::TwoSmallInts(a, b) 
    } 
} 

fn main() { 
    let x = MixedInts::new(2i32); 
    let y = MixedInts::new((2i32, 2i32)); 
} 
+0

Примечание: ржавчина поддерживает перегрузку через признаки; это кажется менее практичным, чем использование Into/From, но оно поддерживается. –

-1

Я бы предложил сделать использование признаков From/Into в стандартной библиотеке.

#[derive(PartialEq, Eq, Debug)] 
enum MixedInts { 
    SmallInt(i32), 
    TwoSmallInts(i32, i32), 
} 

impl From<i32> for MixedInts { 
    fn from(n: i32) -> Self { 
     MixedInts::SmallInt(n) 
    } 
} 

impl From<(i32, i32)> for MixedInts { 
    fn from((a, b): (i32, i32)) -> Self { 
     MixedInts::TwoSmallInts(a, b) 
    } 
} 

fn main() { 
    let x: MixedInts = 2_i32.into(); 
    assert_eq!(x, MixedInts::SmallInt(2)); 

    let y: MixedInts = (2_i32, 2_i32).into(); 
    assert_eq!(y, MixedInts::TwoSmallInts(2, 2)); 
} 

example on Rust Playground

+0

Не могли бы вы пояснить, как это отличается от [существующего ответа, который предполагает использование 'From' и' Into'] (http://stackoverflow.com/a/42238787/155423)? – Shepmaster

+0

Единственное отличие заключается в том, что я заменил код внутри main, чтобы использовать реализации From/Into. – ampron