Printing
When writing a program, it is quite common to print some data to the console, either for the normal process of the program or for debugging purpose. In this chapter, we describe the options you have to print simple and complex data types.
Printing Standard Data Types
Cairo provides two macros to print standard data types:
println!which prints on a new lineprint!with inline printing
Both take a ByteArray string as first parameter (see Data
Types), which can be a simple string to print a message or a string
with placeholders to format the way values are printed.
There are two ways to use these placeholders and both can be mixed:
- empty curly brackets
{}are replaced by values given as parameters to theprint!macro, in the same order. - curly brackets with variable names are directly replaced by the variable value.
Here are some examples:
#[executable]
fn main() {
let a = 10;
let b = 20;
let c = 30;
println!("Hello world!");
println!("{} {} {}", a, b, c); // 10 20 30
println!("{c} {a} {}", b); // 30 10 20
}
print!andprintln!macros use theDisplaytrait under the hood, and are therefore used to print the value of types that implement it. This is the case for basic data types, but not for more complex ones. If you try to print complex data type values with these macros, e.g. for debugging purposes, you will get an error. In that case, you can either manually implement theDisplaytrait for your type or use theDebugtrait (see below).
Formatting
Cairo also provides a useful macro to handle string formatting: format!. This
macro works like println!, but instead of printing the output to the screen,
it returns a ByteArray with the contents. In the following example, we perform
string concatenation using either the + operator or the format! macro. The
version of the code using format! is much easier to read, and the code
generated by the format! macro uses snapshots, so that this call doesn’t take
ownership of any of its parameters.
#[executable]
fn main() {
let s1: ByteArray = "tic";
let s2: ByteArray = "tac";
let s3: ByteArray = "toe";
let s = s1 + "-" + s2 + "-" + s3;
// using + operator consumes the strings, so they can't be used again!
let s1: ByteArray = "tic";
let s2: ByteArray = "tac";
let s3: ByteArray = "toe";
let s = format!("{s1}-{s2}-{s3}"); // s1, s2, s3 are not consumed by format!
// or
let s = format!("{}-{}-{}", s1, s2, s3);
println!("{}", s);
}
Printing Custom Data Types
As previously explained, if you try to print the value of a custom data type
with print! or println! macros, you'll get an error telling you that the
Display trait is not implemented for your custom type:
error: Trait has no implementation in context: core::fmt::Display::<package_name::struct_name>
The println! macro can do many kinds of formatting, and by default, the curly
brackets tell println! to use formatting known as Display - output intended
for direct end user consumption. The primitive types we’ve seen so far implement
Display by default because there’s only one way you’d want to show a 1 or
any other primitive type to a user. But with structs, the way println! should
format the output is less clear because there are more display possibilities: Do
we want commas or not? Do we want to print the curly brackets? Should all the
fields be shown? Due to this ambiguity, Cairo doesn’t try to guess what we want,
and structs don’t have a provided implementation of Display to use with
println! and the {} placeholder.
Here is the Display trait to implement:
trait Display<T> {
fn fmt(self: @T, ref f: Formatter) -> Result<(), Error>;
}
The second parameter f is of type Formatter, which is just a struct
containing a ByteArray, representing the pending result of formatting:
#[derive(Default, Drop)]
pub struct Formatter {
/// The pending result of formatting.
pub buffer: ByteArray,
}
Knowing this, here is an example of how to implement the Display trait for a
custom Point struct:
use core::fmt::{Display, Error, Formatter};
#[derive(Copy, Drop)]
struct Point {
x: u8,
y: u8,
}
impl PointDisplay of Display<Point> {
fn fmt(self: @Point, ref f: Formatter) -> Result<(), Error> {
let str: ByteArray = format!("Point ({}, {})", *self.x, *self.y);
f.buffer.append(@str);
Ok(())
}
}
#[executable]
fn main() {
let p = Point { x: 1, y: 3 };
println!("{}", p); // Point: (1, 3)
}
Cairo also provides the write! and writeln! macros to write formatted
strings in a formatter. Here is a short example using write! macro to
concatenate multiple strings on the same line and then print the result:
use core::fmt::Formatter;
#[executable]
fn main() {
let mut formatter: Formatter = Default::default();
let a = 10;
let b = 20;
write!(formatter, "hello");
write!(formatter, "world");
write!(formatter, " {a} {b}");
println!("{}", formatter.buffer); // helloworld 10 20
}
It is also possible to implement the Display trait for the Point struct
using these macros, as shown here:
use core::fmt::{Display, Error, Formatter};
#[derive(Copy, Drop)]
struct Point {
x: u8,
y: u8,
}
impl PointDisplay of Display<Point> {
fn fmt(self: @Point, ref f: Formatter) -> Result<(), Error> {
let x = *self.x;
let y = *self.y;
writeln!(f, "Point ({x}, {y})")
}
}
#[executable]
fn main() {
let p = Point { x: 1, y: 3 };
println!("{}", p); // Point: (1, 3)
}
Printing complex data types this way might not be ideal as it requires additional steps to use the
print!andprintln!macros. If you need to print complex data types, especially when debugging, use theDebugtrait described below instead.
Print in Hexadecimal
By default, the Display trait prints integer values in decimal. But, as in
Rust, you can use the {:x} notation to print them in hexadecimal.
Under the hood, Cairo implements the LowerHex trait for common types such as
unsigned integers, felt252 and NonZero but also for common Starknet types
such as ContractAddress and ClassHash.
If it makes sense for you, you can also implement the LowerHex trait for your
custom types using the same approach as for the Display trait (see Printing
Custom Data Types).
Print Debug Traces
Cairo provides the Debug trait, which can be derived to print the value of
variables when debugging. Simply add :? within the curly brackets {}
placeholders in a print! or println! macro string.
This trait is very useful and is implemented by default for basic data types. It
can also be simply derived for complex data types using the #[derive(Debug)]
attribute, as long as all types they contain implement it. This eliminates the
need to manually implement extra code to print complex data types.
Note that assert_xx! macros used in tests require the provided values to
implement the Debug trait, as they also print the result in case of assertion
failure.
For more details about the Debug trait and its usage for printing values when
debugging, please refer to the Derivable Traits appendix.