Skip to content

Commit 08c1155

Browse files
committed
auto merge of #7142 : alexcrichton/rust/deriving-zero, r=pcwalton
This allows mass-initialization of large structs without having to specify all the fields. I'm a bit hesitant, but I wanted to get this out there. I don't really like using the `Zero` trait, because it doesn't really make sense for a type like `HashMap` to use `Zero` as the 'blank allocation' trait. In theory there'd be a new trait, but then that's adding cruft to the language which may not necessarily need to be there. I do think that this can be useful, but I only implemented `Zero` on the basic types where I thought it made sense, so it may not be all that usable yet. (opinions?)
2 parents d1927d2 + 893c70d commit 08c1155

File tree

9 files changed

+201
-2
lines changed

9 files changed

+201
-2
lines changed

src/libstd/char.rs

+8-2
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,8 @@ use u32;
1717
use uint;
1818
use unicode::{derived_property, general_category};
1919

20-
#[cfg(not(test))]
21-
use cmp::{Eq, Ord};
20+
#[cfg(not(test))] use cmp::{Eq, Ord};
21+
#[cfg(not(test))] use num::Zero;
2222

2323
/*
2424
Lu Uppercase_Letter an uppercase letter
@@ -328,6 +328,12 @@ impl Ord for char {
328328
fn ge(&self, other: &char) -> bool { *self >= *other }
329329
}
330330

331+
#[cfg(not(test))]
332+
impl Zero for char {
333+
fn zero() -> char { 0 as char }
334+
fn is_zero(&self) -> bool { *self == 0 as char }
335+
}
336+
331337
#[test]
332338
fn test_is_lowercase() {
333339
assert!('a'.is_lowercase());

src/libstd/num/num.rs

+15
Original file line numberDiff line numberDiff line change
@@ -418,6 +418,21 @@ pub fn pow_with_uint<T:NumCast+One+Zero+Copy+Div<T,T>+Mul<T,T>>(radix: uint, pow
418418
total
419419
}
420420

421+
impl<T: Zero> Zero for @mut T {
422+
fn zero() -> @mut T { @mut Zero::zero() }
423+
fn is_zero(&self) -> bool { (**self).is_zero() }
424+
}
425+
426+
impl<T: Zero> Zero for @T {
427+
fn zero() -> @T { @Zero::zero() }
428+
fn is_zero(&self) -> bool { (**self).is_zero() }
429+
}
430+
431+
impl<T: Zero> Zero for ~T {
432+
fn zero() -> ~T { ~Zero::zero() }
433+
fn is_zero(&self) -> bool { (**self).is_zero() }
434+
}
435+
421436
/// Helper function for testing numeric operations
422437
#[cfg(test)]
423438
pub fn test_num<T:Num + NumCast>(ten: T, two: T) {

src/libstd/option.rs

+5
Original file line numberDiff line numberDiff line change
@@ -350,6 +350,11 @@ impl<T:Copy + Zero> Option<T> {
350350
}
351351
}
352352

353+
impl<T> Zero for Option<T> {
354+
fn zero() -> Option<T> { None }
355+
fn is_zero(&self) -> bool { self.is_none() }
356+
}
357+
353358
/// Immutable iterator over an `Option<A>`
354359
pub struct OptionIterator<'self, A> {
355360
priv opt: Option<&'self A>

src/libstd/str.rs

+11
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ use container::Container;
2727
use iter::Times;
2828
use iterator::{Iterator, IteratorUtil, FilterIterator, AdditiveIterator, MapIterator};
2929
use libc;
30+
use num::Zero;
3031
use option::{None, Option, Some};
3132
use old_iter::{BaseIter, EqIter};
3233
use ptr;
@@ -2201,6 +2202,16 @@ impl<'self> Iterator<u8> for StrBytesRevIterator<'self> {
22012202
}
22022203
}
22032204
2205+
impl Zero for ~str {
2206+
fn zero() -> ~str { ~"" }
2207+
fn is_zero(&self) -> bool { self.len() == 0 }
2208+
}
2209+
2210+
impl Zero for @str {
2211+
fn zero() -> @str { @"" }
2212+
fn is_zero(&self) -> bool { self.len() == 0 }
2213+
}
2214+
22042215
#[cfg(test)]
22052216
mod tests {
22062217
use iterator::IteratorUtil;

src/libstd/tuple.rs

+13
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,7 @@ macro_rules! tuple_impls {
135135
pub mod inner {
136136
use clone::Clone;
137137
#[cfg(not(test))] use cmp::*;
138+
#[cfg(not(test))] use num::Zero;
138139

139140
$(
140141
pub trait $cloneable_trait<$($T),+> {
@@ -210,6 +211,18 @@ macro_rules! tuple_impls {
210211
lexical_cmp!($(self.$get_ref_fn(), other.$get_ref_fn()),+)
211212
}
212213
}
214+
215+
#[cfg(not(test))]
216+
impl<$($T:Zero),+> Zero for ($($T),+) {
217+
#[inline]
218+
fn zero() -> ($($T),+) {
219+
($(Zero::zero::<$T>()),+)
220+
}
221+
#[inline]
222+
fn is_zero(&self) -> bool {
223+
$(self.$get_ref_fn().is_zero())&&+
224+
}
225+
}
213226
)+
214227
}
215228
}

src/libstd/vec.rs

+11
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ use iterator::{Iterator, IteratorUtil};
2323
use iter::FromIter;
2424
use kinds::Copy;
2525
use libc;
26+
use num::Zero;
2627
use old_iter::CopyableIter;
2728
use option::{None, Option, Some};
2829
use ptr::to_unsafe_ptr;
@@ -2628,6 +2629,16 @@ impl<A:Clone> Clone for ~[A] {
26282629
}
26292630
}
26302631

2632+
impl<A> Zero for ~[A] {
2633+
fn zero() -> ~[A] { ~[] }
2634+
fn is_zero(&self) -> bool { self.len() == 0 }
2635+
}
2636+
2637+
impl<A> Zero for @[A] {
2638+
fn zero() -> @[A] { @[] }
2639+
fn is_zero(&self) -> bool { self.len() == 0 }
2640+
}
2641+
26312642
macro_rules! iterator {
26322643
/* FIXME: #4375 Cannot attach documentation/attributes to a macro generated struct.
26332644
(struct $name:ident -> $ptr:ty, $elem:ty) => {

src/libsyntax/ext/deriving/mod.rs

+2
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ pub mod encodable;
3131
pub mod decodable;
3232
pub mod rand;
3333
pub mod to_str;
34+
pub mod zero;
3435

3536
#[path="cmp/eq.rs"]
3637
pub mod eq;
@@ -98,6 +99,7 @@ pub fn expand_meta_deriving(cx: @ExtCtxt,
9899
"Rand" => expand!(rand::expand_deriving_rand),
99100

100101
"ToStr" => expand!(to_str::expand_deriving_to_str),
102+
"Zero" => expand!(zero::expand_deriving_zero),
101103

102104
ref tname => {
103105
cx.span_err(titem.span, fmt!("unknown \

src/libsyntax/ext/deriving/zero.rs

+96
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
// Copyright 2012-2013 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
use core::prelude::*;
12+
13+
use ast::{meta_item, item, expr};
14+
use codemap::span;
15+
use ext::base::ExtCtxt;
16+
use ext::build::AstBuilder;
17+
use ext::deriving::generic::*;
18+
19+
use core::vec;
20+
21+
pub fn expand_deriving_zero(cx: @ExtCtxt,
22+
span: span,
23+
mitem: @meta_item,
24+
in_items: ~[@item])
25+
-> ~[@item] {
26+
let trait_def = TraitDef {
27+
path: Path::new(~["std", "num", "Zero"]),
28+
additional_bounds: ~[],
29+
generics: LifetimeBounds::empty(),
30+
methods: ~[
31+
MethodDef {
32+
name: "zero",
33+
generics: LifetimeBounds::empty(),
34+
explicit_self: None,
35+
args: ~[],
36+
ret_ty: Self,
37+
const_nonmatching: false,
38+
combine_substructure: zero_substructure
39+
},
40+
MethodDef {
41+
name: "is_zero",
42+
generics: LifetimeBounds::empty(),
43+
explicit_self: borrowed_explicit_self(),
44+
args: ~[],
45+
ret_ty: Literal(Path::new(~["bool"])),
46+
const_nonmatching: false,
47+
combine_substructure: |cx, span, substr| {
48+
cs_and(|cx, span, _, _| cx.span_bug(span,
49+
"Non-matching enum \
50+
variant in \
51+
deriving(Zero)"),
52+
cx, span, substr)
53+
}
54+
}
55+
]
56+
};
57+
trait_def.expand(cx, span, mitem, in_items)
58+
}
59+
60+
fn zero_substructure(cx: @ExtCtxt, span: span, substr: &Substructure) -> @expr {
61+
let zero_ident = ~[
62+
cx.ident_of("std"),
63+
cx.ident_of("num"),
64+
cx.ident_of("Zero"),
65+
cx.ident_of("zero")
66+
];
67+
let zero_call = || {
68+
cx.expr_call_global(span, copy zero_ident, ~[])
69+
};
70+
71+
return match *substr.fields {
72+
StaticStruct(_, ref summary) => {
73+
match *summary {
74+
Left(count) => {
75+
if count == 0 {
76+
cx.expr_ident(span, substr.type_ident)
77+
} else {
78+
let exprs = vec::from_fn(count, |_| zero_call());
79+
cx.expr_call_ident(span, substr.type_ident, exprs)
80+
}
81+
}
82+
Right(ref fields) => {
83+
let zero_fields = do fields.map |ident| {
84+
cx.field_imm(span, *ident, zero_call())
85+
};
86+
cx.expr_struct_ident(span, substr.type_ident, zero_fields)
87+
}
88+
}
89+
}
90+
StaticEnum(*) => {
91+
cx.span_fatal(span, "`Zero` cannot be derived for enums, \
92+
only structs")
93+
}
94+
_ => cx.bug("Non-static method in `deriving(Zero)`")
95+
};
96+
}

src/test/run-pass/deriving-zero.rs

+40
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
// Copyright 2012-2013 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
use std::util;
12+
use std::num::Zero;
13+
14+
#[deriving(Zero)]
15+
struct A;
16+
#[deriving(Zero)]
17+
struct B(int);
18+
#[deriving(Zero)]
19+
struct C(int, int);
20+
#[deriving(Zero)]
21+
struct D { a: int }
22+
#[deriving(Zero)]
23+
struct E { a: int, b: int }
24+
25+
#[deriving(Zero)]
26+
struct Lots {
27+
a: ~str,
28+
b: @str,
29+
c: Option<util::NonCopyable>,
30+
d: u8,
31+
e: char,
32+
f: float,
33+
g: (f32, char),
34+
h: ~[util::NonCopyable],
35+
i: @mut (int, int),
36+
}
37+
38+
fn main() {
39+
assert!(Zero::zero::<Lots>().is_zero());
40+
}

0 commit comments

Comments
 (0)