std: Enforce Unicode in fmt::Writer

This commit is an implementation of [RFC 526][rfc] which is a change to alter
the definition of the old `fmt::FormatWriter`. The new trait, renamed to
`Writer`, now only exposes one method `write_str` in order to guarantee that all
implementations of the formatting traits can only produce valid Unicode.

[rfc]: https://github.com/rust-lang/rfcs/blob/master/text/0526-fmt-text-writer.md

One of the primary improvements of this patch is the performance of the
`.to_string()` method by avoiding an almost-always redundant UTF-8 check. This
is a breaking change due to the renaming of the trait as well as the loss of the
`write` method, but migration paths should be relatively easy:

* All usage of `write` should move to `write_str`. If truly binary data was
  being written in an implementation of `Show`, then it will need to use a
  different trait or an altogether different code path.

* All usage of `write!` should continue to work as-is with no modifications.

* All usage of `Show` where implementations just delegate to another should
  continue to work as-is.

[breaking-change]

Closes #20352
This commit is contained in:
Alex Crichton 2014-12-12 10:59:41 -08:00
parent cd614164e6
commit e423fcf0e0
25 changed files with 320 additions and 359 deletions

View file

@ -10,17 +10,17 @@
extern crate serialize;
use std::io;
use std::fmt;
use serialize::{Encodable, Encoder};
pub fn buffer_encode<'a,
T:Encodable<serialize::json::Encoder<'a>,io::IoError>>(
T:Encodable<serialize::json::Encoder<'a>,fmt::Error>>(
to_encode_object: &T)
-> Vec<u8> {
let mut m = Vec::new();
-> String {
let mut m = String::new();
{
let mut encoder =
serialize::json::Encoder::new(&mut m as &mut io::Writer);
serialize::json::Encoder::new(&mut m);
//~^ ERROR `m` does not live long enough
to_encode_object.encode(&mut encoder);
}

View file

@ -1,26 +0,0 @@
// Copyright 2013 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// Previously failed formating invalid utf8.
// cc #16877
// error-pattern:panicked at 'hello<6C>'
struct Foo;
impl std::fmt::Show for Foo {
fn fmt(&self, fmtr:&mut std::fmt::Formatter) -> std::fmt::Result {
// Purge invalid utf8: 0xff
fmtr.write(&[104, 101, 108, 108, 111, 0xff])
}
}
fn main() {
panic!("{}", Foo)
}

View file

@ -15,7 +15,6 @@
use std::io::MemWriter;
use std::fmt;
use std::fmt::FormatWriter;
struct Foo<'a> {
writer: &'a mut (Writer+'a),
@ -24,8 +23,8 @@ struct Foo<'a> {
struct Bar;
impl fmt::FormatWriter for Bar {
fn write(&mut self, _: &[u8]) -> fmt::Result {
impl fmt::Writer for Bar {
fn write_str(&mut self, _: &str) -> fmt::Result {
Ok(())
}
}
@ -41,5 +40,8 @@ fn main() {
println!("ok");
let mut s = Bar;
write!(&mut s, "test");
{
use std::fmt::Writer;
write!(&mut s, "test");
}
}

View file

@ -16,7 +16,6 @@
#![allow(unused_must_use)]
use std::fmt;
use std::io;
struct A;
struct B;
@ -24,17 +23,17 @@ struct C;
impl fmt::LowerHex for A {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.write("aloha".as_bytes())
f.write_str("aloha")
}
}
impl fmt::UpperHex for B {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.write("adios".as_bytes())
f.write_str("adios")
}
}
impl fmt::Show for C {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.pad_integral(true, "", "123".as_bytes())
f.pad_integral(true, "", "123")
}
}
@ -160,18 +159,18 @@ pub fn main() {
// Basic test to make sure that we can invoke the `write!` macro with an
// io::Writer instance.
fn test_write() {
let mut buf = Vec::new();
write!(&mut buf as &mut io::Writer, "{}", 3i);
use std::fmt::Writer;
let mut buf = String::new();
write!(&mut buf, "{}", 3i);
{
let w = &mut buf as &mut io::Writer;
let w = &mut buf;
write!(w, "{foo}", foo=4i);
write!(w, "{}", "hello");
writeln!(w, "{}", "line");
writeln!(w, "{foo}", foo="bar");
}
let s = String::from_utf8(buf).unwrap();
t!(s, "34helloline\nbar\n");
t!(buf, "34helloline\nbar\n");
}
// Just make sure that the macros are defined, there's not really a lot that we
@ -187,14 +186,15 @@ fn test_print() {
// Just make sure that the macros are defined, there's not really a lot that we
// can do with them just yet (to test the output)
fn test_format_args() {
let mut buf = Vec::new();
use std::fmt::Writer;
let mut buf = String::new();
{
let w = &mut buf as &mut io::Writer;
let w = &mut buf;
write!(w, "{}", format_args!("{}", 1i));
write!(w, "{}", format_args!("test"));
write!(w, "{}", format_args!("{test}", test=3i));
}
let s = String::from_utf8(buf).unwrap();
let s = buf;
t!(s, "1test3");
let s = fmt::format(format_args!("hello {}", "world"));

View file

@ -12,7 +12,8 @@ extern crate rbml;
extern crate serialize;
use std::io;
use std::io::{IoError, IoResult, SeekStyle};
use std::fmt;
use std::io::{IoResult, SeekStyle};
use std::slice;
use serialize::{Encodable, Encoder};
@ -37,16 +38,15 @@ enum WireProtocol {
// ...
}
fn encode_json<'a,
T: Encodable<json::Encoder<'a>,
std::io::IoError>>(val: &T,
wr: &'a mut SeekableMemWriter) {
let mut encoder = json::Encoder::new(wr);
val.encode(&mut encoder);
fn encode_json<
T: for<'a> Encodable<json::Encoder<'a>,
fmt::Error>>(val: &T,
wr: &mut SeekableMemWriter) {
write!(wr, "{}", json::as_json(val));
}
fn encode_rbml<'a,
T: Encodable<writer::Encoder<'a, SeekableMemWriter>,
std::io::IoError>>(val: &T,
io::IoError>>(val: &T,
wr: &'a mut SeekableMemWriter) {
let mut encoder = writer::Encoder::new(wr);
val.encode(&mut encoder);

View file

@ -12,7 +12,7 @@
extern crate serialize;
use std::io::IoError;
use std::fmt;
use serialize::{Encoder, Encodable};
use serialize::json;
@ -21,7 +21,7 @@ struct Foo<T> {
}
#[unsafe_destructor]
impl<'a, T: Encodable<json::Encoder<'a>, IoError>> Drop for Foo<T> {
impl<T: for<'a> Encodable<json::Encoder<'a>, fmt::Error>> Drop for Foo<T> {
fn drop(&mut self) {
json::encode(&self.v);
}