Как определить ширину печати для типа, который можно отформатировать? - PullRequest
2 голосов
/ 07 апреля 2020

Если тип реализует Debug, как определить ширину того, что должно быть напечатано из println!("{:?}", ...)?

fn width<T: std::fmt::Debug>(to_print_later: &T) -> usize {
    // what goes here?
}

Желательно, без фактического выделения чего-либо в куче и, безусловно, без фактической печати что-нибудь.

Ответы [ 2 ]

5 голосов
/ 07 апреля 2020

Вы не можете сделать это без фактического выполнения форматирования. Форматирование по умолчанию ленив, и у черт форматирования нет метода, который позволяет запрашивать, какой размер будет.

Кроме того, реализация типа Debug::fmt может выделять или не распределять, поэтому невозможно сказать, что нет распределение произойдет.

Самое простое, что нужно сделать, это создать строку и вернуть длину строки:

fn width(to_print_later: &impl std::fmt::Debug) -> usize {
    format!("{:?}", to_print_later).len()
}

Если вы хотите избежать своего собственного Вы можете написать свой собственный реализатор fmt::Write и отформатировать его, считая только длину:

use std::fmt::{self, Write};

fn width(to_print_later: &impl fmt::Debug) -> usize {
    let mut c = CountingWriter::default();
    write!(&mut c, "{:?}", to_print_later).expect("Exceeded the usize");
    c.0
}

#[derive(Debug, Default)]
struct CountingWriter(usize);

impl fmt::Write for CountingWriter {
    fn write_str(&mut self, s: &str) -> fmt::Result {
        self.0 = usize::checked_add(self.0, s.len()).ok_or(fmt::Error)?;
        Ok(())
    }
}

Это не является гарантированным выигрышем в производительности, поскольку возможно, что реализация Debug::fmt это тяжелая часть, а не распределение. Как обычно, профиль, чтобы знать, что лучше для вашего случая.


Обратите внимание, что я использую Debug в этом ответе, но это относится и к другим чертам, таким как Display и LowerHex.

2 голосов
/ 07 апреля 2020

Вы можете написать свою собственную реализацию std::fmt::Write, которая рассчитывает вместо печати:

use std::fmt::Write;

#[derive(Default)]
struct Counter {
    count: usize,
}

impl Write for Counter {
    fn write_str(&mut self, s: &str) -> std::fmt::Result {
        self.count += s.len();
        Ok(())
    }

    fn write_char(&mut self, c: char) -> std::fmt::Result {
        self.count += c.len_utf8();
        Ok(())
    }
}

fn main() {
    let mut counter = Counter::default();
    write!(counter, "Number of {}: {}", "foo", 42).unwrap();

    println!("Count: {}", counter.count);
}

Это не выделит большую строку, но все же выделит меньшие строки для большинства параметров (потому что они преобразуются в строку перед вызовом write_str).

...