Skip to content

Commit 7721987

Browse files
committed
Future::boxed and Stream::boxed should prevent double boxing
Fixes #511
1 parent 87a86c2 commit 7721987

File tree

6 files changed

+179
-2
lines changed

6 files changed

+179
-2
lines changed

Cargo.toml

+3
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,9 @@ appveyor = { repository = "alexcrichton/futures-rs" }
2020

2121
[dependencies]
2222

23+
[build-dependencies]
24+
regex = "0.2"
25+
2326
[features]
2427
use_std = []
2528
with-deprecated = []

build.rs

+56
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
extern crate regex;
2+
3+
use std::env;
4+
use std::str;
5+
use std::process;
6+
7+
#[allow(dead_code)]
8+
struct RustVersion {
9+
major: u32,
10+
minor: u32,
11+
patch: u32,
12+
nightly: bool,
13+
}
14+
15+
fn rust_version() -> RustVersion {
16+
let rustc = env::var("RUSTC").expect("RUSTC variable is unset");
17+
18+
let command = process::Command::new(rustc)
19+
.args(&["--version"])
20+
.stdin(process::Stdio::null())
21+
.stderr(process::Stdio::inherit())
22+
.stdout(process::Stdio::piped())
23+
.spawn()
24+
.expect("spawn rustc");
25+
26+
let wait = command.wait_with_output().expect("wait for rust");
27+
if !wait.status.success() {
28+
panic!("rustc --version exited with non-zero code");
29+
}
30+
31+
let stdout = str::from_utf8(&wait.stdout).expect("stdout is not UTF-8");
32+
33+
let re = regex::Regex::new(r"^rustc (\d+)\.(\d+)\.(\d+)(-nightly)?").expect("compile regex");
34+
let captures = re.captures(stdout)
35+
.expect(&format!("regex cannot match `rustc --version` output: {:?}", stdout));
36+
37+
let major: u32 = captures.get(1).expect("major").as_str().parse().unwrap();
38+
let minor: u32 = captures.get(2).expect("minor").as_str().parse().unwrap();
39+
let patch: u32 = captures.get(3).expect("patch").as_str().parse().unwrap();
40+
let nightly: bool = captures.get(4).is_some();
41+
42+
RustVersion {
43+
major: major,
44+
minor: minor,
45+
patch: patch,
46+
nightly: nightly,
47+
}
48+
}
49+
50+
fn main() {
51+
let version = rust_version();
52+
53+
if version.nightly {
54+
println!("cargo:rustc-cfg=rust_nightly");
55+
}
56+
}

src/future/mod.rs

+28-1
Original file line numberDiff line numberDiff line change
@@ -308,7 +308,7 @@ pub trait Future {
308308
fn boxed(self) -> BoxFuture<Self::Item, Self::Error>
309309
where Self: Sized + Send + 'static
310310
{
311-
::std::boxed::Box::new(self)
311+
Boxed::boxed(self)
312312
}
313313

314314
/// Map this future's result to a different type, returning a new future of
@@ -1044,3 +1044,30 @@ impl<F> fmt::Debug for ExecuteError<F> {
10441044
}
10451045
}
10461046
}
1047+
1048+
1049+
#[cfg(feature = "use_std")]
1050+
trait Boxed: Future {
1051+
fn boxed(self) -> BoxFuture<Self::Item, Self::Error>;
1052+
}
1053+
1054+
#[cfg(feature = "use_std")]
1055+
impl<F> Boxed for F where F: Future + Sized + Send + 'static {
1056+
#[cfg(rust_nightly)]
1057+
default fn boxed(self) -> BoxFuture<Self::Item, Self::Error> {
1058+
::std::boxed::Box::new(self)
1059+
}
1060+
1061+
#[cfg(not(rust_nightly))]
1062+
fn boxed(self) -> BoxFuture<Self::Item, Self::Error> {
1063+
::std::boxed::Box::new(self)
1064+
}
1065+
}
1066+
1067+
#[cfg(feature = "use_std")]
1068+
#[cfg(rust_nightly)]
1069+
impl<I, E> Boxed for BoxFuture<I, E> {
1070+
fn boxed(self) -> BoxFuture<Self::Item, Self::Error> {
1071+
self
1072+
}
1073+
}

src/lib.rs

+2
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
#![cfg_attr(rust_nightly, feature(specialization))]
2+
13
//! Zero-cost Futures in Rust
24
//!
35
//! This library is an implementation of futures in Rust which aims to provide

src/stream/mod.rs

+28-1
Original file line numberDiff line numberDiff line change
@@ -250,7 +250,7 @@ pub trait Stream {
250250
fn boxed(self) -> BoxStream<Self::Item, Self::Error>
251251
where Self: Sized + Send + 'static,
252252
{
253-
::std::boxed::Box::new(self)
253+
Boxed::boxed(self)
254254
}
255255

256256
/// Converts this stream into a `Future`.
@@ -1059,3 +1059,30 @@ impl<'a, S: ?Sized + Stream> Stream for &'a mut S {
10591059
(**self).poll()
10601060
}
10611061
}
1062+
1063+
1064+
#[cfg(feature = "use_std")]
1065+
trait Boxed: Stream {
1066+
fn boxed(self) -> BoxStream<Self::Item, Self::Error>;
1067+
}
1068+
1069+
#[cfg(feature = "use_std")]
1070+
impl<F> Boxed for F where F: Stream + Sized + Send + 'static {
1071+
#[cfg(rust_nightly)]
1072+
default fn boxed(self) -> BoxStream<Self::Item, Self::Error> {
1073+
::std::boxed::Box::new(self)
1074+
}
1075+
1076+
#[cfg(not(rust_nightly))]
1077+
fn boxed(self) -> BoxStream<Self::Item, Self::Error> {
1078+
::std::boxed::Box::new(self)
1079+
}
1080+
}
1081+
1082+
#[cfg(feature = "use_std")]
1083+
#[cfg(rust_nightly)]
1084+
impl<I, E> Boxed for BoxStream<I, E> {
1085+
fn boxed(self) -> BoxStream<Self::Item, Self::Error> {
1086+
self
1087+
}
1088+
}

tests/boxed.rs

+62
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
#![cfg(rust_nightly)]
2+
3+
extern crate futures;
4+
5+
use futures::Async;
6+
use futures::Poll;
7+
use futures::future::Future;
8+
use futures::stream::Stream;
9+
10+
11+
#[test]
12+
fn future_boxed_prevents_double_boxing() {
13+
struct MyFuture {
14+
r: &'static str,
15+
}
16+
17+
impl Future for MyFuture {
18+
type Item = &'static str;
19+
type Error = ();
20+
21+
fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
22+
Ok(Async::Ready(self.r))
23+
}
24+
}
25+
26+
let f = MyFuture { r: "I'm ready" };
27+
let f = f.boxed();
28+
let ptr = f.as_ref() as *const Future<Item=_, Error=_>;
29+
let f = f.boxed();
30+
let f = f.boxed();
31+
let mut f = f.boxed();
32+
assert_eq!(f.as_ref() as *const Future<Item=_, Error=_>, ptr);
33+
assert_eq!(Ok(Async::Ready("I'm ready")), f.poll());
34+
}
35+
36+
#[test]
37+
fn stream_boxed_prevents_double_boxing() {
38+
struct MyStream {
39+
i: u32,
40+
}
41+
42+
impl Stream for MyStream {
43+
type Item = u32;
44+
type Error = ();
45+
46+
fn poll(&mut self) -> Poll<Option<Self::Item>, Self::Error> {
47+
self.i += 1;
48+
Ok(Async::Ready(Some(self.i)))
49+
}
50+
}
51+
52+
let s = MyStream { i: 0 };
53+
let s = s.boxed();
54+
let ptr = s.as_ref() as *const Stream<Item=_, Error=_>;
55+
let s = s.boxed();
56+
let s = s.boxed();
57+
let mut s = s.boxed();
58+
assert_eq!(s.as_ref() as *const Stream<Item=_, Error=_>, ptr);
59+
assert_eq!(Ok(Async::Ready(Some(1))), s.poll());
60+
assert_eq!(Ok(Async::Ready(Some(2))), s.poll());
61+
assert_eq!(Ok(Async::Ready(Some(3))), s.poll());
62+
}

0 commit comments

Comments
 (0)