Add two new methods to std::fmt::DebugMap
for writing the key and value part of a map entry項目
separately:
#![allow(unused)]
fn main () {
impl <'a , 'b : 'a > DebugMap<'a , 'b > {
pub fn key (&mut self , key: &dyn Debug ) -> &mut Self ;
pub fn value (&mut self , value: &dyn Debug ) -> &mut Self ;
}
}
The format builders available to std::fmt::Debug
implementations実装
through the std::fmt::Formatter
help keep the textual debug representation表現
of Rust structures consistent.構成される
They're also convenient to use and make sure the variousさまざまな
formatting flags are retained when formatting entries.項目
The standard formatting API in std::fmt
is similar似ている、同様の
to serde::ser
:
Debug
-> Serialize
Formatter
-> Serializer
DebugMap
-> SerializeMap
DebugList
-> SerializeSeq
DebugTuple
-> SerializeTuple
/ SerializeTupleStruct
/ SerilizeTupleVariant
DebugStruct
-> SerializeStruct
/ SerializeStructVariant
There's one notable inconsistency though: an implementation実装
of SerializeMap
must support serializing its keys and values independently. This isn't supported by DebugMap
because its entry項目
method takesとる
both a key and a value together. That means it's not possible to write a Serializer
that defers entirely to the format builders.
Addingたす
separate key
and value
methods to DebugMap
will align揃える
it more closely with SerializeMap
, and make it possible to build a Serializer
based基となる、基底(の)
on the standard format builders.
In DebugMap
, an entry項目
is the pair of a key and a value. That means the following Debug
implementation:実装
#![allow(unused)]
fn main () {
use std::fmt;
struct Map ;
impl fmt::Debug for Map {
fn fmt (&self , f: &mut fmt::Formatter) -> fmt::Result {
let mut map = f.debug_map();
map.entry(&"key" , &"value" );
map.finish()
}
}
}
is equivalent等価
to:
#![allow(unused)]
fn main () {
impl fmt::Debug for Map {
fn fmt (&self , f: &mut fmt::Formatter) -> fmt::Result {
let mut map = f.debug_map();
map.key(&"key" ).value(&"value" );
map.finish()
}
}
}
Every call呼び出し
to key
must be directly直接
followed下記の、次に続く、追従する
by a corresponding照応する
call呼び出し
to value
to complete完全な
the entry:項目
#![allow(unused)]
fn main () {
impl fmt::Debug for Map {
fn fmt (&self , f: &mut fmt::Formatter) -> fmt::Result {
let mut map = f.debug_map();
map.key(&1 );
map.key(&2 );
map.finish()
}
}
}
key
must be called呼び出し
before value
:
#![allow(unused)]
fn main () {
impl fmt::Debug for Map {
fn fmt (&self , f: &mut fmt::Formatter) -> fmt::Result {
let mut map = f.debug_map();
map.value(&"value" );
map.key(&"key" );
map.finish()
}
}
}
Each entry項目
must be finished before the map can be finished:
#![allow(unused)]
fn main () {
impl fmt::Debug for Map {
fn fmt (&self , f: &mut fmt::Formatter) -> fmt::Result {
let mut map = f.debug_map();
map.key(&1 );
map.finish()
}
}
}
Any incorrect calls呼び出し
to key
and value
will panic.
Why would you want to use key
and value
directly直接
if they're less convenient than entry項目
? The reason is when the driver of the DebugMap
is a framework like serde
rather than a data structure directly:直接
#![allow(unused)]
fn main () {
struct DebugMap <'a , 'b : 'a >(fmt::DebugMap<'a , 'b >);
impl <'a , 'b : 'a > SerializeMap for DebugMap<'a , 'b > {
type Ok = ();
type Error = Error;
fn serialize_key <T: ?Sized >(&mut self , key: &T) -> Result <(), Self::Error>
where
T: Serialize,
{
self .0 .key(&key.to_debug());
Ok (())
}
fn serialize_value <T: ?Sized >(&mut self , value: &T) -> Result <(), Self::Error>
where
T: Serialize,
{
self .0 .value(&value.to_debug());
Ok (())
}
fn serialize_entry <K: ?Sized , V: ?Sized >(
&mut self ,
key: &K,
value: &V,
) -> Result <(), Self::Error>
where
K: Serialize,
V: Serialize,
{
self .0 .entry(&key.to_debug(), &value.to_debug());
Ok (())
}
fn end (self ) -> Result <Self::Ok , Self::Error> {
self .0 .finish().map_err(Into ::into)
}
}
}
Consumers should prefer calling呼び出し
entry項目
over key
and value
.
The key
and value
methods can be implemented実装する
on DebugMap
by tracking the state of the current entry項目
in a bool
, and splitting the existing entry項目
method into two:
#![allow(unused)]
fn main () {
pub struct DebugMap <'a , 'b : 'a > {
has_key: bool ,
..
}
pub fn debug_map_new <'a , 'b >(fmt: &'a mut fmt::Formatter<'b >) -> DebugMap<'a , 'b > {
DebugMap {
has_key: false ,
..
}
}
impl <'a , 'b : 'a > DebugMap<'a , 'b > {
pub fn entry (&mut self , key: &dyn fmt::Debug , value: &dyn fmt::Debug ) -> &mut DebugMap<'a , 'b > {
self .key(key).value(value)
}
pub fn key (&mut self , key: &dyn fmt::Debug ) -> &mut DebugMap<'a , 'b > {
assert! (!self .has_key, "attempted to begin a new map entry without completing the previous one" );
self .result = self .result.and_then(|_| {
self .has_key = true ;
Ok (())
});
self
}
pub fn value (&mut self , value: &dyn fmt::Debug ) -> &mut DebugMap<'a , 'b > {
assert! (self .has_key, "attempted to format a map value before its key" );
self .result = self .result.and_then(|_| {
self .has_key = false ;
Ok (())
});
self .has_fields = true ;
self
}
pub fn finish (&mut self ) -> fmt::Result {
assert! (!self .has_key, "attempted to finish a map with a partial entry" );
self .result.and_then(|_| self .fmt.write_str("}" ))
}
}
}
The proposed key
and value
methods are't immediately直後に、直接的に
useful for Debug
implementors that are able to call呼び出し
entry項目
instead. This creates a decision point where there wasn't one before. The proposed implementation実装
is also going to be less efficient効率のよい
than the one that exists now because it introduces a few conditionals.条件付き、条件的
On balance, the additional追加の
key
and value
methods are a small and unsurprising addition追加
that enables a setセットする、集合
of use-cases that weren't possible before, and aligns more closely with serde
.
The universal alternative代わりのもの、選択肢
of simply not doing this leaves consumers that do need to format map keys independently of values with a few options:
Write an alternative代わりのもの、選択肢
implementation実装
of the format builders. The output from this alternative代わりのもの、選択肢
implementation実装
would need to be kept reasonably in-sync with the one in the standard library. It doesn't change very frequently, but does from time to time. It would also have to takeとる
the same care as the standard library implementation実装
to retain formatting flags when working with entries.項目
Buffer keys and format them together with values when the whole entry項目
is available. Unless the key is guaranteed保証する
to live until the value is supplied (meaning it probably needs to be 'static
) then the key will need to be formatted into a string first. This means allocating確保する
(though the cost could be amortized over the whole map) and potentially losing formatting flags when buffering.
Another alternative代わりのもの、選択肢
is to avoid避ける、回避する
panicking if the sequence連なり、並び
of entries項目
doesn't follow下記の、次に続く、追従する
the expected pattern of key
then value
. Instead, DebugMap
could make a best-effort attempt to represent keys without values and values without keys. However, this approach has the drawback of maskingマスク、隠す
incorrect Debug
implementations,実装
may produce産出する
a surprising output and doesn't reduce the complexity of the implementation実装
(we'd still need to tell whether a key should be followed下記の、次に続く、追従する
by a :
separator or a ,
).
The serde::ser::SerializeMap
API (and libserialize::Encoder
for what it's worth) requires map keys and values can be serialized independently. SerializeMap
provides与える
a serialize_entry
method, which is similar似ている、同様の
to the existing DebugMap::entry
, but is only supposed to be used as an optimization.
The internal implementation実装
could optimize最適化する
the entry項目
method to avoid避ける、回避する
a few redundant checks.