2 #![allow(unused_imports)]
3 #![allow(non_snake_case)]
5 // ASCII art shape renderer. Demonstrates traits, impls, operator overloading,
6 // non-copyable struct, unit testing. To run execute: rustc --test shapes.rs &&
9 // Rust's std library is tightly bound to the language itself so it is
10 // automatically linked in. However the extra library is designed to be
11 // optional (for code that must run on constrained environments like embedded
12 // devices or special environments like kernel code) so it must be explicitly
15 // Extern mod controls linkage. Use controls the visibility of names to modules
16 // that are already linked in. Using WriterUtil allows us to use the write_line
20 use std
::iter
::repeat
;
23 // Represents a position on a canvas.
24 #[derive(Copy, Clone)]
30 // Represents an offset on a canvas. (This has the same structure as a Point.
31 // but different semantics).
32 #[derive(Copy, Clone)]
38 #[derive(Copy, Clone)]
44 // Contains the information needed to do shape rendering via ASCII art.
49 lines
: Vec
<Vec
<char> > ,
51 // This struct can be quite large so we'll disable copying: developers need
52 // to either pass these structs around via references or move them.
55 impl Drop
for AsciiArt
{
59 // It's common to define a constructor sort of function to create struct instances.
60 // If there is a canonical constructor it is typically named the same as the type.
61 // Other constructor sort of functions are typically named from_foo, from_bar, etc.
62 fn AsciiArt(width
: usize, height
: usize, fill
: char) -> AsciiArt
{
63 // Build a vector of vectors containing blank characters for each position in
65 let lines
= vec
![vec
!['
.'
; width
]; height
];
67 // Rust code often returns values by omitting the trailing semi-colon
68 // instead of using an explicit return statement.
69 AsciiArt {width: width, height: height, fill: fill, lines: lines}
72 // Methods particular to the AsciiArt struct.
74 fn add_pt(&mut self, x
: isize, y
: isize) {
75 if x
>= 0 && x
< self.width
as isize {
76 if y
>= 0 && y
< self.height
as isize {
77 // Note that numeric types don't implicitly convert to each other.
81 // Vector subscripting will normally copy the element, but &v[i]
82 // will return a reference which is what we need because the
84 // 1) potentially large
85 // 2) needs to be modified
86 let row
= &mut self.lines
[v
];
93 // Allows AsciiArt to be converted to a string using the libcore ToString trait.
94 // Note that the %s fmt! specifier will not call this automatically.
95 impl fmt
::Display
for AsciiArt
{
96 fn fmt(&self, f
: &mut fmt
::Formatter
) -> fmt
::Result
{
97 // Convert each line into a string.
98 let lines
= self.lines
.iter()
99 .map(|line
| line
.iter().cloned().collect())
100 .collect
::<Vec
<String
>>();
102 // Concatenate the lines together using a new-line.
103 write
!(f
, "{}", lines
.join("\n"))
107 // This is similar to an interface in other languages: it defines a protocol which
108 // developers can implement for arbitrary concrete types.
110 fn add_point(&mut self, shape
: Point
);
111 fn add_rect(&mut self, shape
: Rect
);
113 // Unlike interfaces traits support default implementations.
114 // Got an ICE as soon as I added this method.
115 fn add_points(&mut self, shapes
: &[Point
]) {
116 for pt
in shapes {self.add_point(*pt)}
;
120 // Here we provide an implementation of the Canvas methods for AsciiArt.
121 // Other implementations could also be provided (e.g., for PDF or Apple's Quartz)
122 // and code can use them polymorphically via the Canvas trait.
123 impl Canvas
for AsciiArt
{
124 fn add_point(&mut self, shape
: Point
) {
125 self.add_pt(shape
.x
, shape
.y
);
128 fn add_rect(&mut self, shape
: Rect
) {
129 // Add the top and bottom lines.
130 for x
in shape
.top_left
.x
..shape
.top_left
.x
+ shape
.size
.width
{
131 self.add_pt(x
, shape
.top_left
.y
);
132 self.add_pt(x
, shape
.top_left
.y
+ shape
.size
.height
- 1);
135 // Add the left and right lines.
136 for y
in shape
.top_left
.y
..shape
.top_left
.y
+ shape
.size
.height
{
137 self.add_pt(shape
.top_left
.x
, y
);
138 self.add_pt(shape
.top_left
.x
+ shape
.size
.width
- 1, y
);
143 // Rust's unit testing framework is currently a bit under-developed so we'll use
144 // this little helper.
145 pub fn check_strs(actual
: &str, expected
: &str) -> bool
{
146 if actual
!= expected
{
147 println
!("Found:\n{}\nbut expected\n{}", actual
, expected
);
154 fn test_ascii_art_ctor() {
155 let art
= AsciiArt(3, 3, '
*'
);
156 assert
!(check_strs(&art
.to_string(), "...\n...\n..."));
161 let mut art
= AsciiArt(3, 3, '
*'
);
165 assert
!(check_strs(&art
.to_string(), "*..\n...\n.*."));
170 let mut art
= AsciiArt(4, 4, '
*'
);
171 art
.add_rect(Rect {top_left: Point {x: 0, y: 0}
, size
: Size {width: 4, height: 4}
});
172 art
.add_point(Point {x: 2, y: 2}
);
173 assert
!(check_strs(&art
.to_string(), "****\n*..*\n*.**\n****"));
177 test_ascii_art_ctor();