Rollup merge of #37574 - ollie27:cow_add, r=alexcrichton

Fix issues with the Add/AddAssign impls for Cow<str>

* Correct the stability attributes.
* Make Add and AddAssign actually behave the same.
* Use String::with_capacity when allocating a new string.
* Fix the tests.
This commit is contained in:
Alex Crichton 2016-11-04 16:49:31 -07:00
commit 638436e55f
3 changed files with 146 additions and 56 deletions

View File

@ -17,6 +17,7 @@ use core::hash::{Hash, Hasher};
use core::ops::{Add, AddAssign, Deref}; use core::ops::{Add, AddAssign, Deref};
use fmt; use fmt;
use string::String;
use self::Cow::*; use self::Cow::*;
@ -284,48 +285,60 @@ impl<'a, T: ?Sized + ToOwned> AsRef<T> for Cow<'a, T> {
} }
} }
#[stable(feature = "cow_add", since = "1.13.0")] #[stable(feature = "cow_add", since = "1.14.0")]
impl<'a> Add<&'a str> for Cow<'a, str> { impl<'a> Add<&'a str> for Cow<'a, str> {
type Output = Cow<'a, str>; type Output = Cow<'a, str>;
fn add(self, rhs: &'a str) -> Self { #[inline]
if self == "" { fn add(mut self, rhs: &'a str) -> Self::Output {
Cow::Borrowed(rhs) self += rhs;
} else if rhs == "" { self
self
} else {
Cow::Owned(self.into_owned() + rhs)
}
} }
} }
#[stable(feature = "cow_add", since = "1.13.0")] #[stable(feature = "cow_add", since = "1.14.0")]
impl<'a> Add<Cow<'a, str>> for Cow<'a, str> { impl<'a> Add<Cow<'a, str>> for Cow<'a, str> {
type Output = Cow<'a, str>; type Output = Cow<'a, str>;
fn add(self, rhs: Cow<'a, str>) -> Self { #[inline]
if self == "" { fn add(mut self, rhs: Cow<'a, str>) -> Self::Output {
rhs self += rhs;
} else if rhs == "" { self
self }
}
#[stable(feature = "cow_add", since = "1.14.0")]
impl<'a> AddAssign<&'a str> for Cow<'a, str> {
fn add_assign(&mut self, rhs: &'a str) {
if self.is_empty() {
*self = Cow::Borrowed(rhs)
} else if rhs.is_empty() {
return;
} else { } else {
Cow::Owned(self.into_owned() + rhs.borrow()) if let Cow::Borrowed(lhs) = *self {
let mut s = String::with_capacity(lhs.len() + rhs.len());
s.push_str(lhs);
*self = Cow::Owned(s);
}
self.to_mut().push_str(rhs);
} }
} }
} }
#[stable(feature = "cow_add", since = "1.13.0")] #[stable(feature = "cow_add", since = "1.14.0")]
impl<'a> AddAssign<&'a str> for Cow<'a, str> {
fn add_assign(&mut self, rhs: &'a str) {
if rhs == "" { return; }
self.to_mut().push_str(rhs);
}
}
#[stable(feature = "cow_add", since = "1.13.0")]
impl<'a> AddAssign<Cow<'a, str>> for Cow<'a, str> { impl<'a> AddAssign<Cow<'a, str>> for Cow<'a, str> {
fn add_assign(&mut self, rhs: Cow<'a, str>) { fn add_assign(&mut self, rhs: Cow<'a, str>) {
if rhs == "" { return; } if self.is_empty() {
self.to_mut().push_str(rhs.borrow()); *self = rhs
} else if rhs.is_empty() {
return;
} else {
if let Cow::Borrowed(lhs) = *self {
let mut s = String::with_capacity(lhs.len() + rhs.len());
s.push_str(lhs);
*self = Cow::Owned(s);
}
self.to_mut().push_str(&rhs);
}
} }
} }

View File

@ -1,4 +1,4 @@
// Copyright 2012-2013-2014 The Rust Project Developers. See the COPYRIGHT // Copyright 2016 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at // file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT. // http://rust-lang.org/COPYRIGHT.
// //
@ -12,54 +12,130 @@ use std::borrow::Cow;
// check that Cow<'a, str> implements addition // check that Cow<'a, str> implements addition
#[test] #[test]
fn check_cow_add() { fn check_cow_add_cow() {
borrowed1 = Cow::Borrowed("Hello, "); let borrowed1 = Cow::Borrowed("Hello, ");
borrowed2 = Cow::Borrowed("World!"); let borrowed2 = Cow::Borrowed("World!");
borrow_empty = Cow::Borrowed(""); let borrow_empty = Cow::Borrowed("");
owned1 = Cow::Owned("Hi, ".into()); let owned1: Cow<str> = Cow::Owned(String::from("Hi, "));
owned2 = Cow::Owned("Rustaceans!".into()); let owned2: Cow<str> = Cow::Owned(String::from("Rustaceans!"));
owned_empty = Cow::Owned("".into()); let owned_empty: Cow<str> = Cow::Owned(String::new());
assert_eq!("Hello, World!", borrowed1 + borrowed2); assert_eq!("Hello, World!", borrowed1.clone() + borrowed2.clone());
assert_eq!("Hello, Rustaceans!", borrowed1 + owned2); assert_eq!("Hello, Rustaceans!", borrowed1.clone() + owned2.clone());
assert_eq!("Hello, World!", owned1 + borrowed2); assert_eq!("Hi, World!", owned1.clone() + borrowed2.clone());
assert_eq!("Hello, Rustaceans!", owned1 + owned2); assert_eq!("Hi, Rustaceans!", owned1.clone() + owned2.clone());
if let Cow::Owned(_) = borrowed1 + borrow_empty { if let Cow::Owned(_) = borrowed1.clone() + borrow_empty.clone() {
panic!("Adding empty strings to a borrow should note allocate"); panic!("Adding empty strings to a borrow should note allocate");
} }
if let Cow::Owned(_) = borrow_empty + borrowed1 { if let Cow::Owned(_) = borrow_empty.clone() + borrowed1.clone() {
panic!("Adding empty strings to a borrow should note allocate"); panic!("Adding empty strings to a borrow should note allocate");
} }
if let Cow::Owned(_) = borrowed1 + owned_empty { if let Cow::Owned(_) = borrowed1.clone() + owned_empty.clone() {
panic!("Adding empty strings to a borrow should note allocate"); panic!("Adding empty strings to a borrow should note allocate");
} }
if let Cow::Owned(_) = owned_empty + borrowed1 { if let Cow::Owned(_) = owned_empty.clone() + borrowed1.clone() {
panic!("Adding empty strings to a borrow should note allocate"); panic!("Adding empty strings to a borrow should note allocate");
} }
} }
fn check_cow_add_assign() { #[test]
borrowed1 = Cow::Borrowed("Hello, "); fn check_cow_add_str() {
borrowed2 = Cow::Borrowed("World!"); let borrowed = Cow::Borrowed("Hello, ");
borrow_empty = Cow::Borrowed(""); let borrow_empty = Cow::Borrowed("");
owned1 = Cow::Owned("Hi, ".into()); let owned: Cow<str> = Cow::Owned(String::from("Hi, "));
owned2 = Cow::Owned("Rustaceans!".into()); let owned_empty: Cow<str> = Cow::Owned(String::new());
owned_empty = Cow::Owned("".into());
let borrowed1clone = borrowed1.clone(); assert_eq!("Hello, World!", borrowed.clone() + "World!");
borrowed1clone += borrow_empty;
assert_eq!((&borrowed1clone).as_ptr(), (&borrowed1).as_ptr());
borrowed1clone += owned_empty; assert_eq!("Hi, World!", owned.clone() + "World!");
assert_eq!((&borrowed1clone).as_ptr(), (&borrowed1).as_ptr());
if let Cow::Owned(_) = borrowed.clone() + "" {
panic!("Adding empty strings to a borrow should note allocate");
}
if let Cow::Owned(_) = borrow_empty.clone() + "Hello, " {
panic!("Adding empty strings to a borrow should note allocate");
}
if let Cow::Owned(_) = owned_empty.clone() + "Hello, " {
panic!("Adding empty strings to a borrow should note allocate");
}
}
#[test]
fn check_cow_add_assign_cow() {
let mut borrowed1 = Cow::Borrowed("Hello, ");
let borrowed2 = Cow::Borrowed("World!");
let borrow_empty = Cow::Borrowed("");
let mut owned1: Cow<str> = Cow::Owned(String::from("Hi, "));
let owned2: Cow<str> = Cow::Owned(String::from("Rustaceans!"));
let owned_empty: Cow<str> = Cow::Owned(String::new());
let mut s = borrowed1.clone();
s += borrow_empty.clone();
assert_eq!("Hello, ", s);
if let Cow::Owned(_) = s {
panic!("Adding empty strings to a borrow should note allocate");
}
let mut s = borrow_empty.clone();
s += borrowed1.clone();
assert_eq!("Hello, ", s);
if let Cow::Owned(_) = s {
panic!("Adding empty strings to a borrow should note allocate");
}
let mut s = borrowed1.clone();
s += owned_empty.clone();
assert_eq!("Hello, ", s);
if let Cow::Owned(_) = s {
panic!("Adding empty strings to a borrow should note allocate");
}
let mut s = owned_empty.clone();
s += borrowed1.clone();
assert_eq!("Hello, ", s);
if let Cow::Owned(_) = s {
panic!("Adding empty strings to a borrow should note allocate");
}
owned1 += borrowed2; owned1 += borrowed2;
borrowed1 += owned2; borrowed1 += owned2;
assert_eq!("Hello, World!", owned1); assert_eq!("Hi, World!", owned1);
assert_eq!("Hello, Rustaceans!", borrowed1); assert_eq!("Hello, Rustaceans!", borrowed1);
} }
#[test]
fn check_cow_add_assign_str() {
let mut borrowed = Cow::Borrowed("Hello, ");
let borrow_empty = Cow::Borrowed("");
let mut owned: Cow<str> = Cow::Owned(String::from("Hi, "));
let owned_empty: Cow<str> = Cow::Owned(String::new());
let mut s = borrowed.clone();
s += "";
assert_eq!("Hello, ", s);
if let Cow::Owned(_) = s {
panic!("Adding empty strings to a borrow should note allocate");
}
let mut s = borrow_empty.clone();
s += "World!";
assert_eq!("World!", s);
if let Cow::Owned(_) = s {
panic!("Adding empty strings to a borrow should note allocate");
}
let mut s = owned_empty.clone();
s += "World!";
assert_eq!("World!", s);
if let Cow::Owned(_) = s {
panic!("Adding empty strings to a borrow should note allocate");
}
owned += "World!";
borrowed += "World!";
assert_eq!("Hi, World!", owned);
assert_eq!("Hello, World!", borrowed);
}

View File

@ -42,6 +42,7 @@ mod bench;
mod binary_heap; mod binary_heap;
mod btree; mod btree;
mod cow_str;
mod enum_set; mod enum_set;
mod fmt; mod fmt;
mod linked_list; mod linked_list;