2015-12-17 14 views
11

Sto leggendo il libro Go linguaggio di programmazione e nella sua descrizione del pacchetto di errore e l'interfacciaPerché ErrorString una struttura, non è una stringa

package errors 

type error interface { 
    Error() string 
} 

func New(text string) error { return &errorString{text} } 

type errorString struct { text string } 

func (e *errorString) Error() string { return e.text } 

si dice

Il tipo sottostante di errorString è una struct, non una stringa, per proteggere la sua rappresentazione da aggiornamenti involontari (o premeditati).

Cosa significa? Il pacchetto non nasconderà il tipo sottostante dal momento che errorString non viene esportato?

Aggiornamento Ecco il codice di prova che ho usato attuazione errorString utilizzando un string invece. Nota che quando provi ad usarlo da un altro pacchetto, non puoi semplicemente assegnare una stringa come un errore.

package testerr 

type Error interface { 
     Error() string 
} 

func New(text string) Error { 
     return errorString(text) 
} 

type errorString string 

func (e errorString) Error() string { return string(e) } 

E testarlo con i codici suggeriti

func main() { 
    err := errors.New("foo") 
    err = "bar" 
    fmt.Prinln(err) 
} 

finirà per produrre un errore durante la compilazione

cannot use "bar" (type string) as type testerr.Error in assignment: string does not implement testerr.Error (missing Error method)

Naturalmente c'è un aspetto negativo a questo dato diverso gli errori che capita di avere la stessa stringa di errore valuteranno per essere uguali a quelli che non vogliamo.

risposta

5

La spiegazione del libro su "proteggere la rappresentazione da aggiornamenti involontari" mi sembra fuorviante. Se errorString è una struct o una stringa, il messaggio di errore è ancora una stringa e una stringa è immutable by specification.

Questo non è un dibattito sull'unicità neanche. Ad esempio, errors.New("EOF") == io.EOF restituisce false, sebbene entrambi gli errori abbiano esattamente lo stesso messaggio sottostante. Lo stesso vale anche se errorString era una stringa, a patto che errors.New sarebbe tornare un puntatore ad esso (see my example.)

Si potrebbe dire una struct attuando error è idiomatica dato che è anche il modo in libreria standard introduce errori personalizzati . Date un'occhiata a SyntaxError dal pacchetto encoding/json:

type SyntaxError struct { 
     Offset int64 // error occurred after reading Offset bytes 
     // contains filtered or unexported fields 
} 

func (e *SyntaxError) Error() string { return e.msg } 

(source)

Inoltre, una struttura che implementa l'interfaccia error non ha implicazioni di prestazioni e non consuma più memoria su un'implementazione stringa. Vedi Go Data Structures.

+0

Controllare la modifica. – shebaw

+1

@shebaw vedo. Ho lasciato l'interfaccia 'error' fuori dall'equazione. Ho rivisto la mia risposta. –

+0

Immagino che e la seconda spiegazione fornita dal libro sia, se avessimo fatto in modo che 'errorString' fosse una stringa, allora qualsiasi errore che capita di avere la stessa stringa di errore valuterà come' True'. Es: 'io.EOF == errors.New (" EOF ")' che non è corretto. – shebaw

3

La confezione testerr funziona abbastanza bene, ma perde una delle principali caratteristiche del pacchetto di errore "struct-based" standard: quello di non-uguaglianza:

package main 
import ("fmt"; "testerr"; "errors") 

func main() { 
    a := testerr.New("foo") 
    b := testerr.New("foo") 
    fmt.Println(a == b) // true 

    c := errors.New("foo") 
    d := errors.New("foo") 
    fmt.Println(c == d) // false 
} 

Con errorString essere una stringa di pianura diversi errori con il lo stesso contenuto di stringhe diventa uguale.Il codice originale utilizza un puntatore per struct e ogni New alloca una nuova struttura in modo che i diversi valori restituiti da New siano diversi se confrontati con == nonostante il testo di errore uguale.

Nessun compilatore è autorizzato a produrre lo stesso puntatore qui. E questa caratteristica di "diverse chiamate a Nuovo produce diversi valori di errore" è importante per prevenire l'uguaglianza non intenzionale degli errori. Il tuo testerr può essere modificato per ottenere questa proprietà avendo *errorString attuare Error. Prova: hai bisogno di un temporaneo per prendere l'indirizzo di. "Sente" sbagliato. Si potrebbe immaginare un compilatore di fantasia che interiorizzi i valori di stringa e possa restituire lo stesso puntatore (poiché punta alla stessa stringa interiorizzata) che interromperà questa bella proprietà di disuguaglianza.

Problemi correlati