Skip to content

Commit 76c3e8a

Browse files
committed
Add an SendStr type
A SendStr is a string that can hold either a ~str or a &'static str. This can be useful as an optimization when an allocation is sometimes needed but the common case is statically known. Possible use cases include Maps with both static and owned keys, or propagating error messages across task boundaries. SendStr implements most basic traits in a way that hides the fact that it is an enum; in particular things like order and equality are only determined by the content of the wrapped strings. Replaced std::rt:logging::SendableString with SendStr Added tests for using an SendStr as key in Hash- and Treemaps
1 parent 3e1803f commit 76c3e8a

File tree

9 files changed

+406
-16
lines changed

9 files changed

+406
-16
lines changed

src/libstd/logging.rs

+4-3
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,8 @@
1313
use option::*;
1414
use os;
1515
use rt;
16-
use rt::logging::{Logger, StdErrLogger, OwnedString};
16+
use rt::logging::{Logger, StdErrLogger};
17+
use send_str::SendStrOwned;
1718

1819
/// Turns on logging to stdout globally
1920
pub fn console_on() {
@@ -56,12 +57,12 @@ fn newsched_log_str(msg: ~str) {
5657
match optional_task {
5758
Some(local) => {
5859
// Use the available logger
59-
(*local).logger.log(OwnedString(msg));
60+
(*local).logger.log(SendStrOwned(msg));
6061
}
6162
None => {
6263
// There is no logger anywhere, just write to stderr
6364
let mut logger = StdErrLogger;
64-
logger.log(OwnedString(msg));
65+
logger.log(SendStrOwned(msg));
6566
}
6667
}
6768
}

src/libstd/prelude.rs

+1
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ pub use path::PosixPath;
6666
pub use path::WindowsPath;
6767
pub use ptr::RawPtr;
6868
pub use ascii::{Ascii, AsciiCast, OwnedAsciiCast, AsciiStr, ToBytesConsume};
69+
pub use send_str::{SendStr, SendStrOwned, SendStrStatic, IntoSendStr};
6970
pub use str::{Str, StrVector, StrSlice, OwnedStr};
7071
pub use from_str::FromStr;
7172
pub use to_bytes::IterBytes;

src/libstd/rt/logging.rs

+5-10
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ use str::raw::from_c_str;
1717
use u32;
1818
use vec::ImmutableVector;
1919
use cast::transmute;
20+
use send_str::{SendStr, SendStrOwned, SendStrStatic};
2021

2122
struct LogDirective {
2223
name: Option<~str>,
@@ -168,32 +169,26 @@ fn update_log_settings(crate_map: *u8, settings: ~str) {
168169
}
169170
}
170171

171-
/// Represent a string with `Send` bound.
172-
pub enum SendableString {
173-
OwnedString(~str),
174-
StaticString(&'static str)
175-
}
176-
177172
pub trait Logger {
178-
fn log(&mut self, msg: SendableString);
173+
fn log(&mut self, msg: SendStr);
179174
}
180175

181176
pub struct StdErrLogger;
182177

183178
impl Logger for StdErrLogger {
184-
fn log(&mut self, msg: SendableString) {
179+
fn log(&mut self, msg: SendStr) {
185180
use io::{Writer, WriterUtil};
186181

187182
if !should_log_console() {
188183
return;
189184
}
190185

191186
let s: &str = match msg {
192-
OwnedString(ref s) => {
187+
SendStrOwned(ref s) => {
193188
let slc: &str = *s;
194189
slc
195190
},
196-
StaticString(s) => s,
191+
SendStrStatic(s) => s,
197192
};
198193

199194
// Truncate the string

src/libstd/send_str.rs

+223
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,223 @@
1+
// Copyright 2013 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
//! `SendStr` definition and trait implementations
12+
13+
use clone::{Clone, DeepClone};
14+
use cmp::{Eq, TotalEq, Ord, TotalOrd, Equiv};
15+
use cmp::{Ordering, Less};
16+
use container::Container;
17+
use default::Default;
18+
use str::{Str, StrSlice};
19+
use to_str::ToStr;
20+
use to_bytes::{IterBytes, Cb};
21+
22+
/// A SendStr is a string that can hold either a ~str or a &'static str.
23+
/// This can be useful as an optimization when an allocation is sometimes
24+
/// needed but the common case is statically known.
25+
pub enum SendStr {
26+
SendStrOwned(~str),
27+
SendStrStatic(&'static str)
28+
}
29+
30+
impl SendStr {
31+
/// Returns `true` if this `SendStr` wraps an owned string
32+
#[inline]
33+
pub fn is_owned(&self) -> bool {
34+
match *self {
35+
SendStrOwned(_) => true,
36+
SendStrStatic(_) => false
37+
}
38+
}
39+
40+
/// Returns `true` if this `SendStr` wraps an static string
41+
#[inline]
42+
pub fn is_static(&self) -> bool {
43+
match *self {
44+
SendStrOwned(_) => false,
45+
SendStrStatic(_) => true
46+
}
47+
}
48+
}
49+
50+
/// Trait for moving into an `SendStr`
51+
pub trait IntoSendStr {
52+
/// Moves self into an `SendStr`
53+
fn into_send_str(self) -> SendStr;
54+
}
55+
56+
impl IntoSendStr for ~str {
57+
#[inline]
58+
fn into_send_str(self) -> SendStr { SendStrOwned(self) }
59+
}
60+
61+
impl IntoSendStr for &'static str {
62+
#[inline]
63+
fn into_send_str(self) -> SendStr { SendStrStatic(self) }
64+
}
65+
66+
/*
67+
Section: string trait impls
68+
69+
`SendStr `should behave like a normal string, so we don't derive.
70+
*/
71+
72+
impl ToStr for SendStr {
73+
#[inline]
74+
fn to_str(&self) -> ~str { self.as_slice().to_owned() }
75+
}
76+
77+
impl Eq for SendStr {
78+
#[inline]
79+
fn eq(&self, other: &SendStr) -> bool {
80+
self.as_slice().equals(&other.as_slice())
81+
}
82+
}
83+
84+
impl TotalEq for SendStr {
85+
#[inline]
86+
fn equals(&self, other: &SendStr) -> bool {
87+
self.as_slice().equals(&other.as_slice())
88+
}
89+
}
90+
91+
impl Ord for SendStr {
92+
#[inline]
93+
fn lt(&self, other: &SendStr) -> bool { self.cmp(other) == Less }
94+
}
95+
96+
impl TotalOrd for SendStr {
97+
#[inline]
98+
fn cmp(&self, other: &SendStr) -> Ordering {
99+
self.as_slice().cmp(&other.as_slice())
100+
}
101+
}
102+
103+
impl<'self, S: Str> Equiv<S> for SendStr {
104+
#[inline]
105+
fn equiv(&self, other: &S) -> bool {
106+
self.as_slice().equals(&other.as_slice())
107+
}
108+
}
109+
110+
impl Str for SendStr {
111+
#[inline]
112+
fn as_slice<'r>(&'r self) -> &'r str {
113+
match *self {
114+
SendStrOwned(ref s) => s.as_slice(),
115+
// XXX: Borrowchecker doesn't recognize lifetime as static unless prompted
116+
// SendStrStatic(s) => s.as_slice()
117+
SendStrStatic(s) => {let tmp: &'static str = s; tmp}
118+
}
119+
}
120+
121+
#[inline]
122+
fn into_owned(self) -> ~str {
123+
match self {
124+
SendStrOwned(s) => s,
125+
SendStrStatic(s) => s.to_owned()
126+
}
127+
}
128+
}
129+
130+
impl Container for SendStr {
131+
#[inline]
132+
fn len(&self) -> uint { self.as_slice().len() }
133+
}
134+
135+
impl Clone for SendStr {
136+
#[inline]
137+
fn clone(&self) -> SendStr {
138+
match *self {
139+
SendStrOwned(ref s) => SendStrOwned(s.to_owned()),
140+
SendStrStatic(s) => SendStrStatic(s)
141+
}
142+
}
143+
}
144+
145+
impl DeepClone for SendStr {
146+
#[inline]
147+
fn deep_clone(&self) -> SendStr {
148+
match *self {
149+
SendStrOwned(ref s) => SendStrOwned(s.to_owned()),
150+
SendStrStatic(s) => SendStrStatic(s)
151+
}
152+
}
153+
}
154+
155+
impl Default for SendStr {
156+
#[inline]
157+
fn default() -> SendStr { SendStrStatic("") }
158+
}
159+
160+
impl IterBytes for SendStr {
161+
#[inline]
162+
fn iter_bytes(&self, lsb0: bool, f: Cb) -> bool {
163+
match *self {
164+
SendStrOwned(ref s) => s.iter_bytes(lsb0, f),
165+
SendStrStatic(s) => s.iter_bytes(lsb0, f)
166+
}
167+
}
168+
}
169+
170+
#[cfg(test)]
171+
mod tests {
172+
use clone::{Clone, DeepClone};
173+
use cmp::{TotalEq, Ord, TotalOrd, Equiv};
174+
use cmp::Equal;
175+
use container::Container;
176+
use default::Default;
177+
use send_str::{SendStrOwned, SendStrStatic};
178+
use str::Str;
179+
use to_str::ToStr;
180+
181+
#[test]
182+
fn test_send_str() {
183+
let s = SendStrStatic("abcde");
184+
assert_eq!(s.len(), 5);
185+
assert_eq!(s.as_slice(), "abcde");
186+
assert_eq!(s.to_str(), ~"abcde");
187+
assert!(s.equiv(&@"abcde"));
188+
assert!(s.lt(&SendStrOwned(~"bcdef")));
189+
assert_eq!(SendStrStatic(""), Default::default());
190+
assert!(s.is_static());
191+
assert!(!s.is_owned());
192+
193+
assert_eq!(s.clone(), s.clone());
194+
assert_eq!(s.clone().into_owned(), ~"abcde");
195+
assert_eq!(s.clone().deep_clone(), s.clone());
196+
197+
let o = SendStrOwned(~"abcde");
198+
assert_eq!(o.len(), 5);
199+
assert_eq!(o.as_slice(), "abcde");
200+
assert_eq!(o.to_str(), ~"abcde");
201+
assert!(o.equiv(&@"abcde"));
202+
assert!(o.lt(&SendStrStatic("bcdef")));
203+
assert_eq!(SendStrOwned(~""), Default::default());
204+
assert!(!o.is_static());
205+
assert!(o.is_owned());
206+
207+
assert_eq!(o.clone(), o.clone());
208+
assert_eq!(o.clone().into_owned(), ~"abcde");
209+
assert_eq!(o.clone().deep_clone(), o.clone());
210+
211+
assert_eq!(s.cmp(&o), Equal);
212+
assert!(s.equals(&o));
213+
assert!(s.equiv(&o));
214+
assert_eq!(o.cmp(&s), Equal);
215+
assert!(o.equals(&s));
216+
assert!(o.equiv(&s));
217+
218+
assert_eq!("abcde".into_send_str(), SendStrStatic("abcde"));
219+
assert_eq!((~"abcde").into_send_str(), SendStrStatic("abcde"));
220+
assert_eq!("abcde".into_send_str(), SendStrOwned(~"abcde"));
221+
assert_eq!((~"abcde").into_send_str(), SendStrOwned(~"abcde"));
222+
}
223+
}

src/libstd/std.rs

+1
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,7 @@ pub mod str;
121121

122122
#[path = "str/ascii.rs"]
123123
pub mod ascii;
124+
pub mod send_str;
124125

125126
pub mod ptr;
126127
pub mod owned;

src/libstd/str.rs

+16-1
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ use unstable::raw::{Repr, Slice};
3737
use vec;
3838
use vec::{OwnedVector, OwnedCopyableVector, ImmutableVector, MutableVector};
3939
use default::Default;
40+
use send_str::{SendStr, SendStrOwned};
4041

4142
/*
4243
Section: Conditions
@@ -130,10 +131,12 @@ impl ToStr for ~str {
130131
#[inline]
131132
fn to_str(&self) -> ~str { self.to_owned() }
132133
}
134+
133135
impl<'self> ToStr for &'self str {
134136
#[inline]
135137
fn to_str(&self) -> ~str { self.to_owned() }
136138
}
139+
137140
impl ToStr for @str {
138141
#[inline]
139142
fn to_str(&self) -> ~str { self.to_owned() }
@@ -330,7 +333,6 @@ impl<'self> DoubleEndedIterator<char> for CharIterator<'self> {
330333
}
331334
}
332335

333-
334336
/// External iterator for a string's characters and their byte offsets.
335337
/// Use with the `std::iterator` module.
336338
#[deriving(Clone)]
@@ -1355,6 +1357,7 @@ pub trait StrSlice<'self> {
13551357
fn to_owned(&self) -> ~str;
13561358
fn to_managed(&self) -> @str;
13571359
fn to_utf16(&self) -> ~[u16];
1360+
fn to_send_str(&self) -> SendStr;
13581361
fn is_char_boundary(&self, index: uint) -> bool;
13591362
fn char_range_at(&self, start: uint) -> CharRange;
13601363
fn char_at(&self, i: uint) -> char;
@@ -1869,6 +1872,11 @@ impl<'self> StrSlice<'self> for &'self str {
18691872
u
18701873
}
18711874
1875+
#[inline]
1876+
fn to_send_str(&self) -> SendStr {
1877+
SendStrOwned(self.to_owned())
1878+
}
1879+
18721880
/// Returns false if the index points into the middle of a multi-byte
18731881
/// character sequence.
18741882
#[inline]
@@ -2428,6 +2436,7 @@ mod tests {
24282436
use vec;
24292437
use vec::{Vector, ImmutableVector, CopyableVector};
24302438
use cmp::{TotalOrd, Less, Equal, Greater};
2439+
use send_str::{SendStrOwned, SendStrStatic};
24312440
24322441
#[test]
24332442
fn test_eq() {
@@ -3724,6 +3733,12 @@ mod tests {
37243733
let xs = bytes!("hello", 0xff).to_owned();
37253734
assert_eq!(from_utf8_owned_opt(xs), None);
37263735
}
3736+
3737+
#[test]
3738+
fn test_to_send_str() {
3739+
assert_eq!("abcde".to_send_str(), SendStrStatic("abcde"));
3740+
assert_eq!("abcde".to_send_str(), SendStrOwned(~"abcde"));
3741+
}
37273742
}
37283743
37293744
#[cfg(test)]

src/libstd/sys.rs

+3-2
Original file line numberDiff line numberDiff line change
@@ -140,7 +140,8 @@ pub fn begin_unwind_(msg: *c_char, file: *c_char, line: size_t) -> ! {
140140
use rt::in_green_task_context;
141141
use rt::task::Task;
142142
use rt::local::Local;
143-
use rt::logging::{Logger, OwnedString};
143+
use rt::logging::Logger;
144+
use send_str::SendStrOwned;
144145
use str::Str;
145146

146147
unsafe {
@@ -163,7 +164,7 @@ pub fn begin_unwind_(msg: *c_char, file: *c_char, line: size_t) -> ! {
163164
msg, file, line as int)
164165
};
165166

166-
task.logger.log(OwnedString(msg));
167+
task.logger.log(SendStrOwned(msg));
167168
}
168169
} else {
169170
rterrln!("failed in non-task context at '%s', %s:%i",

0 commit comments

Comments
 (0)