Surprisingly few know about the built-in pretty-printer. In the book, there is only a short passage that mentions {:#?} in passing.

It aligns structs and enums based on nested positions and is automatically derived with Debug . Recently, I’m working with some custom AST. Here is the print out for println("{:#?}", typed_ast) :

TyProgram ( [ TyUseStmt ( TyUseStmt { mod_name : "conv" , imported_names : [ "Conv2d" , "Dropout2d" , "maxpool2d" ] } ), TyUseStmt ( TyUseStmt { mod_name : "nonlin" , imported_names : [ "relu" ] } ), TyUseStmt ( TyUseStmt { mod_name : "lin" , imported_names : [ "Linear" ] } ), TyNodeDecl ( TyNodeDecl { name : "Mnist" , ty_sig : ([ ! 6 , ! 7 , ! 8 , ! 9 ] -> [ ! 6 , < 10 > ]) } ), TyWeightsDecl ( TyWeightsDecl { name : "Mnist" , ty_sig : ([ ! 6 , ! 7 , ! 8 , ! 9 ] -> [ ! 6 , < 10 > ]), inits : [ TyWeightsAssign { name : "conv1" , ty : ' 11 , mod_name : "Conv2d" , fn_ty : ' 10 , fn_name : "new" , fn_args : [ TyFnAppArg { name : "in_ch" , arg : TyExpr { items : TyInteger ( ' 12 , 1 ), ty : ' 13 } }, TyFnAppArg { name : "out_ch" , arg : TyExpr { items : TyInteger ( ' 14 , 10 ), ty : ' 15 } }, TyFnAppArg { name : "kernel_size" , arg : TyExpr { items : TyInteger ( ' 16 , 5 ), ty : ' 17 } } ] }, TyWeightsAssign { name : "conv2" , ty : ' 19 , mod_name : "Conv2d" , fn_ty : ' 18 , fn_name : "new" , fn_args : [ TyFnAppArg { name : "in_ch" , arg : TyExpr { items : TyInteger ( ' 20 , 10 ), ty : ' 21 } }, TyFnAppArg { name : "out_ch" , arg : TyExpr { items : TyInteger ( ' 22 , 20 ), ty : ' 23 } }, TyFnAppArg { name : "kernel_size" , arg : TyExpr { items : TyInteger ( ' 24 , 5 ), ty : ' 25 } } ] }, TyWeightsAssign { name : "dropout" , ty : ' 27 , mod_name : "Dropout2d" , fn_ty : ' 26 , fn_name : "new" , fn_args : [ TyFnAppArg { name : "p" , arg : TyExpr { items : TyFloat ( ' 28 , 0.5 ), ty : ' 29 } } ] }, TyWeightsAssign { name : "fc1" , ty : ' 31 , mod_name : "Linear" , fn_ty : ' 30 , fn_name : "new" , fn_args : [] }, TyWeightsAssign { name : "fc2" , ty : ' 33 , mod_name : "Linear" , fn_ty : ' 32 , fn_name : "new" , fn_args : [] } ] } ), TyGraphDecl ( TyGraphDecl { name : "Mnist" , ty_sig : ([ ! 6 , ! 7 , ! 8 , ! 9 ] -> [ ! 6 , < 10 > ]), fns : [ TyFnDecl { name : "new" , fn_params : [], fn_ty : ' 34 , param_ty : ' 35 , return_ty : ([ ! 6 , ! 7 , ! 8 , ! 9 ] -> [ ! 6 , < 10 > ]), func_block : TyBlock { stmts : TyList ( [ TyStmt { items : TyList ( [ TyExpr { items : TyFnApp ( TyFnApp { mod_name : Some ( "fc1" ), name : "init_normal" , args : [ TyFnAppArg { name : "std" , arg : TyExpr { items : TyFloat ( ' 36 , 1.0 ), ty : ' 37 } } ], ret_ty : ' 38 } ), ty : ' 39 } ] ) }, TyStmt { items : TyList ( [ TyExpr { items : TyFnApp ( TyFnApp { mod_name : Some ( "fc2" ), name : "init_normal" , args : [ TyFnAppArg { name : "std" , arg : TyExpr { items : TyFloat ( ' 40 , 1.0 ), ty : ' 41 } } ], ret_ty : ' 42 } ), ty : ' 43 } ] ) } ] ), ret : TyExpr { items : TyIdent ( ([ ! 6 , ! 7 , ! 8 , ! 9 ] -> [ ! 6 , < 10 > ]), "self" ), ty : ' 44 } } }, TyFnDecl { name : "forward" , fn_params : [ TyFnDeclParam { name : "b" , ty_sig : [ ! 6 , ! 7 , ! 8 , ! 9 ] } ], fn_ty : ' 46 , param_ty : ' 47 , return_ty : [ ! 6 , < 10 > ], func_block : TyBlock { stmts : TyList ( [] ), ret : TyExpr { items : TyFnApp ( TyFnApp { mod_name : None , name : "log_softmax" , args : [ TyFnAppArg { name : "x" , arg : TyFnApp ( TyFnApp { mod_name : Some ( "self" ), name : "fc2" , args : [ TyFnAppArg { name : "x" , arg : TyFnApp ( TyFnApp { mod_name : None , name : "relu" , args : [ TyFnAppArg { name : "x" , arg : TyFnApp ( TyFnApp { mod_name : None , name : "fc1" , args : [ TyFnAppArg { name : "x" , arg : TyViewFn ( TyViewFn { ty : [ ! 6 , < 320 > ], arg : TyFnAppArg { name : "x" , arg : TyFnApp ( TyFnApp { mod_name : None , name : "maxpool2d" , args : [ TyFnAppArg { name : "x" , arg : TyFnApp ( TyFnApp { mod_name : None , name : "dropout" , args : [ TyFnAppArg { name : "x" , arg : TyFnApp ( TyFnApp { mod_name : None , name : "conv2" , args : [ TyFnAppArg { name : "x" , arg : TyFnApp ( TyFnApp { mod_name : None , name : "maxpool2d" , args : [ TyFnAppArg { name : "x" , arg : TyFnApp ( TyFnApp { mod_name : None , name : "conv1" , args : [ TyFnAppArg { name : "x" , arg : TyIdent ( ' 45 , "b" ) } ], ret_ty : ' 48 } ) }, TyFnAppArg { name : "kernel_size" , arg : TyExpr { items : TyInteger ( ' 49 , 2 ), ty : ' 50 } } ], ret_ty : ' 51 } ) } ], ret_ty : ' 52 } ) } ], ret_ty : ' 53 } ) }, TyFnAppArg { name : "kernel_size" , arg : TyExpr { items : TyInteger ( ' 54 , 2 ), ty : ' 55 } } ], ret_ty : ' 56 } ) } } ) } ], ret_ty : ' 57 } ) } ], ret_ty : ' 58 } ) } ], ret_ty : ' 59 } ) }, TyFnAppArg { name : "dim" , arg : TyExpr { items : TyInteger ( ' 60 , 1 ), ty : ' 61 } } ], ret_ty : ' 62 } ), ty : ' 63 } } }, TyFnDecl { name : "fc2" , fn_params : [ TyFnDeclParam { name : "x" , ty_sig : ' 64 } ], fn_ty : ' 65 , param_ty : ' 66 , return_ty : [ ! 6 , < 10 > ], func_block : TyBlock { stmts : TyList ( [] ), ret : TyExpr { items : TyFnApp ( TyFnApp { mod_name : None , name : "relu" , args : [ TyFnAppArg { name : "x" , arg : TyFnApp ( TyFnApp { mod_name : None , name : "fc2" , args : [ TyFnAppArg { name : "x" , arg : TyIdent ( ' 64 , "x" ) } ], ret_ty : ' 67 } ) } ], ret_ty : ' 68 } ), ty : ' 69 } } }, TyFnDecl { name : "test" , fn_params : [ TyFnDeclParam { name : "x" , ty_sig : ' 70 } ], fn_ty : ' 71 , param_ty : ' 72 , return_ty : [], func_block : TyBlock { stmts : TyList ( [] ), ret : TyExpr { items : TyFnApp ( TyFnApp { mod_name : None , name : "relu" , args : [ TyFnAppArg { name : "x" , arg : TyFnApp ( TyFnApp { mod_name : None , name : "fc2" , args : [ TyFnAppArg { name : "x" , arg : TyIdent ( ' 70 , "x" ) } ], ret_ty : ' 73 } ) } ], ret_ty : ' 74 } ), ty : ' 75 } } } ] } ) ] )

Also, it is not mutually exclusive with impl Debug for individual components, for example:

impl Debug for Type { fn fmt ( & self , f : & mut Formatter ) -> Result < (), Error > { use self :: Type :: * ; match self { Unit => write! ( f , "()" ), INT => write! ( f , "int" ), BOOL => write! ( f , "bool" ), VAR ( ref t_id ) => write! ( f , "'{:?}" , t_id ), DIM ( ref t_id ) => write! ( f , "!{:?}" , t_id ), ResolvedDim ( ref d ) => write! ( f , "<{}>" , d ), FUN ( ref p , ref r ) => write! ( f , "({:?} -> {:?})" , p , r ), TSR ( ref dims ) => { if dims .len () > 0 { write! ( f , "[" ) ? ; for i in dims [ 0 .. dims .len () - 1 ] .iter () { write! ( f , "{:?}, " , i ) ? ; } write! ( f , "{:?}]" , dims [ dims .len () - 1 ]) } else { write! ( f , "[]" ) } } } } }

Conclusion

In this click bait article, I introduced the pretty printer flag that few Rust programmers know about.