For the record, a self-plug:
#lang racket
(require struct-plus-plus)
(struct foo-guard (bar baz)
(foo-spp++ #:bar 'yeah #:baz '(buddy))))
#:guard: cpu time: 3335 real time: 3361 gc time: 90
struct/contract: cpu time: 217 real time: 218 gc time: 3
struct++: cpu time: 1255 real time: 1262 gc time: 23
The struct++ version is slower at creation but gives you more security guarantees later on in the form of functional setters, business rules, and wrapper functions to normalize data. For example:
; This accepts a symbol or string and forces it to string.
(struct++ baz-spp ([foo (or/c symbol? string?) ~a]) #:transparent)
(display "Result: ") (println (baz-spp++ #:foo 'bob))
Result: (baz-spp "bob")
; Any struct++ struct will throw an exception if given invalid data
(struct++ bar-spp ([foo integer?]))
(display "Throws: ") (bar-spp++ #:foo 'bob)
Throws:
; bar-spp++: contract violation
; expected: integer?
; given: 'bob
; in: the #:foo argument of
; (-> #:foo integer? bar-spp?)
; contract from: (function bar-spp++)
; blaming: /Users/dstorrs/bmtc_dev/app/test.rkt
; (assuming the contract is correct)
; at: /Users/dstorrs/bmtc_dev/app/test.rkt
; Context:
; "/Users/dstorrs/bmtc_dev/app/test.rkt":1:1 [running body]
; Example of more heavily verified struct. It will accept a string or
; symbol for name and force it to string. It will round the age down
; so it shows only years. It will default the 'gender' field to the
; symbol 'unknown. It will make a database connection and verify that
; the department ID is valid. (In practice you would want to check
; this against an in-RAM table instead of going to disk.)
;
(define (dbh) "mock function that should return a database handle")
(struct++ person ([name (or/c symbol? string?) ~a]
[age positive? (compose inexact->exact floor)]
[(gender 'unknown) (or/c 'male 'female 'other 'unknown)]
[user-id exact-positive-integer?])
(#:rule ("department-id exists in the database"
#:check (user-id)
[(not (null? (query-rows (dbh) ; get database handle
"SELECT id FROM users WHERE id=$1"
user-id)))]))
#:transparent)
(display "Result: ")(println (person++ #:name 'bob #:age 18.5 #:user-id 2))
Result: (person "bob" 18 'unknown 2)