2014-07-18 9 views
12

a Rust, struct tupla con un solo campo possono essere creati come la seguente:implementare automaticamente i tratti di tipo chiuso per newtypes Rust (struct tupla con un solo campo)

struct Centimeters(i32); 

voglio fare operazioni aritmetiche di base con Centimeters senza estrarre i loro valori "interni" ogni volta con la corrispondenza del modello e senza implementare i caratteri Add, Sub, ... e gli operatori di overloading.

Quello che voglio fare è:

let a = Centimeters(100); 
let b = Centimeters(200); 
assert_eq!(a + a, b); 

risposta

13

c'è un modo per farlo senza estrarre i loro valori "interni" ogni volta con pattern matching, e senza l'attuazione del Add, Sub, .. . Tratti e operatori di sovraccarico?

No, l'unico modo è di implementare manualmente i tratti. Ruggine non ha un valore equivalente a su tipi di wrapper per implementare automaticamente qualsiasi tipo di classe/tratto che il tipo avvolto implementa (e con l'attuale configurazione di Rust #[derive] come una semplice trasformazione AST, implementandola come Haskell è . essenzialmente impossibile)

per abbreviare il processo, è possibile utilizzare una macro:

use std::ops::{Add, Sub}; 

macro_rules! obvious_impl { 
    (impl $trait_: ident for $type_: ident { fn $method: ident }) => { 
     impl $trait_<$type_> for $type_ { 
      type Output = $type_; 

      fn $method(self, $type_(b): $type_) -> $type_ { 
       let $type_(a) = self; 
       $type_(a.$method(&b)) 
      } 
     } 
    } 
} 

#[derive(Eq, PartialEq, Ord, PartialOrd, Clone, Debug)] 
pub struct Centimeters(i32); 

obvious_impl! { impl Add for Centimeters { fn add } } 
obvious_impl! { impl Sub for Centimeters { fn sub } } 

#[derive(Eq, PartialEq, Ord, PartialOrd, Clone, Debug)] 
pub struct Inches(i32); 

obvious_impl! { impl Add for Inches { fn add } } 
obvious_impl! { impl Sub for Inches { fn sub } } 

fn main() { 
    let a = Centimeters(100); 
    let b = Centimeters(200); 
    let c = Inches(10); 
    let d = Inches(20); 
    println!("{:?} {:?}", a + b, c + d); // Centimeters(300) Inches(30) 
    // error: 
    // a + c; 
} 

playpen

ho emulato il normale impl sintassi nella macro per rendere evidente ciò che sta accadendo ju st osservando la macro invocazione (es. riducendo la necessità di guardare alla definizione della macro), e anche per mantenere la naturalezza della ricerca di Rust: se stai cercando tratti su Centimeters, basta grep per for Centimeters e troverai queste invocazioni di macro insieme ai normali impl s.

Se si accede il contenuto del tipo Centimeters molto, si potrebbe considerare l'utilizzo di una struttura adeguata con un campo per definire l'involucro:

struct Centimeters { amt: i32 } 

Questo permette di scrivere self.amt invece di dover fare la corrispondenza del modello. Puoi anche definire una funzione come fn cm(x: i32) -> Centimeters { Centimeters { amt: x } }, chiamata come cm(100), per evitare la verbosità di costruire una struttura completa.

È inoltre possibile accedere ai valori interni di una struttura di tupla utilizzando la sintassi .0, .1.

+1

Ero quasi sicuro che i macro fossero la strada da percorrere, ma non li ho ancora padroneggiati :) Se si suggeriscono le strutture appropriate, quale è esattamente il caso d'uso per le strutture one-field? –

+2

@ anonymous_user_13 Intendi una struttura tupla a un campo? La sintassi di costruzione predefinita è più gradevole e il fatto di non dover pensare a un nome per il campo sono gli unici vantaggi a cui riesco a pensare. – huon

0

Per Rust versione 1.10.0, mi sembra che i tipi di alias si adattano perfettamente alla situazione che si sta descrivendo. Semplicemente danno un tipo a un nome diverso.

Diciamo che tutti i centimetri sono u32 s. Poi ho potuto solo utilizzare il codice

type Centimeters = u32; 

Ogni tratto che u32 ha, Centimeters avrebbe automaticamente. Ciò non elimina la possibilità di aggiungere Centimeters a Inches. Se fai attenzione, non ti serviranno tipi diversi.

+4

* Se fai attenzione, non ti serviranno tipi diversi. * - Se sei abbastanza attento non hai bisogno di un linguaggio come Rust^_ ^. I tipi dovrebbero aiutare il programmatore. – Shepmaster

+0

concordato solo offrendo un'idea potente – 5hammer

+1

D è andato più in là lungo questa rotta introducendo sia _type alias_ (come sopra) e _new types_ (simile, ma nessuna conversione implicita). – dhardy

4

Ho effettuato il derive_more crate per questo problema. Può derivare molti tratti per le strutture di cui gli elementi li implementano.

Problemi correlati