Auto merge of #21919 - alexcrichton:rollup, r=alexcrichton
This commit is contained in:
commit
d6c15d9b2d
98 changed files with 5887 additions and 453 deletions
|
@ -2432,6 +2432,8 @@ The currently implemented features of the reference compiler are:
|
|||
* `simd` - Allows use of the `#[simd]` attribute, which is overly simple and
|
||||
not the SIMD interface we want to expose in the long term.
|
||||
|
||||
* `staged_api` - Allows usage of stability markers and `#![staged_api]` in a crate
|
||||
|
||||
* `struct_inherit` - Allows using struct inheritance, which is barely
|
||||
implemented and will probably be removed. Don't use this.
|
||||
|
||||
|
@ -2459,6 +2461,11 @@ The currently implemented features of the reference compiler are:
|
|||
which is considered wildly unsafe and will be
|
||||
obsoleted by language improvements.
|
||||
|
||||
* `unmarked_api` - Allows use of items within a `#![staged_api]` crate
|
||||
which have not been marked with a stability marker.
|
||||
Such items should not be allowed by the compiler to exist,
|
||||
so if you need this there probably is a compiler bug.
|
||||
|
||||
* `associated_types` - Allows type aliases in traits. Experimental.
|
||||
|
||||
If a feature is promoted to a language feature, then all existing programs will
|
||||
|
|
|
@ -45,22 +45,18 @@
|
|||
|
||||
#![stable(feature = "rust1", since = "1.0.0")]
|
||||
|
||||
use core::prelude::*;
|
||||
|
||||
use core::any::Any;
|
||||
use core::clone::Clone;
|
||||
use core::cmp::{PartialEq, PartialOrd, Eq, Ord, Ordering};
|
||||
use core::cmp::Ordering;
|
||||
use core::default::Default;
|
||||
use core::error::{Error, FromError};
|
||||
use core::fmt;
|
||||
use core::hash::{self, Hash};
|
||||
use core::iter::Iterator;
|
||||
use core::marker::Sized;
|
||||
use core::mem;
|
||||
use core::ops::{Deref, DerefMut};
|
||||
use core::option::Option;
|
||||
use core::ptr::Unique;
|
||||
use core::raw::TraitObject;
|
||||
use core::result::Result::{Ok, Err};
|
||||
use core::result::Result;
|
||||
|
||||
/// A value that represents the heap. This is the default place that the `box` keyword allocates
|
||||
/// into when no place is supplied.
|
||||
|
@ -296,18 +292,20 @@ impl<T: ?Sized> DerefMut for Box<T> {
|
|||
fn deref_mut(&mut self) -> &mut T { &mut **self }
|
||||
}
|
||||
|
||||
impl<'a, T> Iterator for Box<Iterator<Item=T> + 'a> {
|
||||
type Item = T;
|
||||
|
||||
fn next(&mut self) -> Option<T> {
|
||||
(**self).next()
|
||||
}
|
||||
|
||||
fn size_hint(&self) -> (usize, Option<usize>) {
|
||||
(**self).size_hint()
|
||||
}
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
impl<I: Iterator + ?Sized> Iterator for Box<I> {
|
||||
type Item = I::Item;
|
||||
fn next(&mut self) -> Option<I::Item> { (**self).next() }
|
||||
fn size_hint(&self) -> (usize, Option<usize>) { (**self).size_hint() }
|
||||
}
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
impl<I: DoubleEndedIterator + ?Sized> DoubleEndedIterator for Box<I> {
|
||||
fn next_back(&mut self) -> Option<I::Item> { (**self).next_back() }
|
||||
}
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
impl<I: ExactSizeIterator + ?Sized> ExactSizeIterator for Box<I> {}
|
||||
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
impl<'a, E: Error + 'a> FromError<E> for Box<Error + 'a> {
|
||||
fn from_error(err: E) -> Box<Error + 'a> {
|
||||
Box::new(err)
|
||||
|
|
|
@ -13,6 +13,8 @@
|
|||
|
||||
#![allow(missing_docs)]
|
||||
|
||||
pub use self::Entry::*;
|
||||
|
||||
use core::prelude::*;
|
||||
|
||||
use core::cmp::Ordering;
|
||||
|
@ -66,6 +68,32 @@ pub struct VecMap<V> {
|
|||
v: Vec<Option<V>>,
|
||||
}
|
||||
|
||||
/// A view into a single entry in a map, which may either be vacant or occupied.
|
||||
#[unstable(feature = "collections",
|
||||
reason = "precise API still under development")]
|
||||
pub enum Entry<'a, V:'a> {
|
||||
/// A vacant Entry
|
||||
Vacant(VacantEntry<'a, V>),
|
||||
/// An occupied Entry
|
||||
Occupied(OccupiedEntry<'a, V>),
|
||||
}
|
||||
|
||||
/// A vacant Entry.
|
||||
#[unstable(feature = "collections",
|
||||
reason = "precise API still under development")]
|
||||
pub struct VacantEntry<'a, V:'a> {
|
||||
map: &'a mut VecMap<V>,
|
||||
index: usize,
|
||||
}
|
||||
|
||||
/// An occupied Entry.
|
||||
#[unstable(feature = "collections",
|
||||
reason = "precise API still under development")]
|
||||
pub struct OccupiedEntry<'a, V:'a> {
|
||||
map: &'a mut VecMap<V>,
|
||||
index: usize,
|
||||
}
|
||||
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
impl<V> Default for VecMap<V> {
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
|
@ -485,6 +513,119 @@ impl<V> VecMap<V> {
|
|||
let result = &mut self.v[*key];
|
||||
result.take()
|
||||
}
|
||||
|
||||
/// Gets the given key's corresponding entry in the map for in-place manipulation.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use std::collections::VecMap;
|
||||
/// use std::collections::vec_map::Entry;
|
||||
///
|
||||
/// let mut count: VecMap<u32> = VecMap::new();
|
||||
///
|
||||
/// // count the number of occurrences of numbers in the vec
|
||||
/// for x in vec![1, 2, 1, 2, 3, 4, 1, 2, 4].iter() {
|
||||
/// match count.entry(*x) {
|
||||
/// Entry::Vacant(view) => {
|
||||
/// view.insert(1);
|
||||
/// },
|
||||
/// Entry::Occupied(mut view) => {
|
||||
/// let v = view.get_mut();
|
||||
/// *v += 1;
|
||||
/// },
|
||||
/// }
|
||||
/// }
|
||||
///
|
||||
/// assert_eq!(count[1], 3);
|
||||
/// ```
|
||||
#[unstable(feature = "collections",
|
||||
reason = "precise API still under development")]
|
||||
pub fn entry(&mut self, key: usize) -> Entry<V> {
|
||||
// FIXME(Gankro): this is basically the dumbest implementation of
|
||||
// entry possible, because weird non-lexical borrows issues make it
|
||||
// completely insane to do any other way. That said, Entry is a border-line
|
||||
// useless construct on VecMap, so it's hardly a big loss.
|
||||
if self.contains_key(&key) {
|
||||
Occupied(OccupiedEntry {
|
||||
map: self,
|
||||
index: key,
|
||||
})
|
||||
} else {
|
||||
Vacant(VacantEntry {
|
||||
map: self,
|
||||
index: key,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
impl<'a, V> Entry<'a, V> {
|
||||
#[unstable(feature = "collections",
|
||||
reason = "matches collection reform v2 specification, waiting for dust to settle")]
|
||||
/// Returns a mutable reference to the entry if occupied, or the VacantEntry if vacant
|
||||
pub fn get(self) -> Result<&'a mut V, VacantEntry<'a, V>> {
|
||||
match self {
|
||||
Occupied(entry) => Ok(entry.into_mut()),
|
||||
Vacant(entry) => Err(entry),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, V> VacantEntry<'a, V> {
|
||||
/// Sets the value of the entry with the VacantEntry's key,
|
||||
/// and returns a mutable reference to it.
|
||||
#[unstable(feature = "collections",
|
||||
reason = "matches collection reform v2 specification, waiting for dust to settle")]
|
||||
pub fn insert(self, value: V) -> &'a mut V {
|
||||
let index = self.index;
|
||||
self.map.insert(index, value);
|
||||
&mut self.map[index]
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, V> OccupiedEntry<'a, V> {
|
||||
/// Gets a reference to the value in the entry.
|
||||
#[unstable(feature = "collections",
|
||||
reason = "matches collection reform v2 specification, waiting for dust to settle")]
|
||||
pub fn get(&self) -> &V {
|
||||
let index = self.index;
|
||||
&self.map[index]
|
||||
}
|
||||
|
||||
/// Gets a mutable reference to the value in the entry.
|
||||
#[unstable(feature = "collections",
|
||||
reason = "matches collection reform v2 specification, waiting for dust to settle")]
|
||||
pub fn get_mut(&mut self) -> &mut V {
|
||||
let index = self.index;
|
||||
&mut self.map[index]
|
||||
}
|
||||
|
||||
/// Converts the entry into a mutable reference to its value.
|
||||
#[unstable(feature = "collections",
|
||||
reason = "matches collection reform v2 specification, waiting for dust to settle")]
|
||||
pub fn into_mut(self) -> &'a mut V {
|
||||
let index = self.index;
|
||||
&mut self.map[index]
|
||||
}
|
||||
|
||||
/// Sets the value of the entry with the OccupiedEntry's key,
|
||||
/// and returns the entry's old value.
|
||||
#[unstable(feature = "collections",
|
||||
reason = "matches collection reform v2 specification, waiting for dust to settle")]
|
||||
pub fn insert(&mut self, value: V) -> V {
|
||||
let index = self.index;
|
||||
self.map.insert(index, value).unwrap()
|
||||
}
|
||||
|
||||
/// Takes the value of the entry out of the map, and returns it.
|
||||
#[unstable(feature = "collections",
|
||||
reason = "matches collection reform v2 specification, waiting for dust to settle")]
|
||||
pub fn remove(self) -> V {
|
||||
let index = self.index;
|
||||
self.map.remove(&index).unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
|
@ -783,7 +924,7 @@ mod test_map {
|
|||
use prelude::*;
|
||||
use core::hash::{hash, SipHasher};
|
||||
|
||||
use super::VecMap;
|
||||
use super::{VecMap, Occupied, Vacant};
|
||||
|
||||
#[test]
|
||||
fn test_get_mut() {
|
||||
|
@ -1135,6 +1276,57 @@ mod test_map {
|
|||
|
||||
map[4];
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_entry(){
|
||||
let xs = [(1, 10), (2, 20), (3, 30), (4, 40), (5, 50), (6, 60)];
|
||||
|
||||
let mut map: VecMap<i32> = xs.iter().map(|&x| x).collect();
|
||||
|
||||
// Existing key (insert)
|
||||
match map.entry(1) {
|
||||
Vacant(_) => unreachable!(),
|
||||
Occupied(mut view) => {
|
||||
assert_eq!(view.get(), &10);
|
||||
assert_eq!(view.insert(100), 10);
|
||||
}
|
||||
}
|
||||
assert_eq!(map.get(&1).unwrap(), &100);
|
||||
assert_eq!(map.len(), 6);
|
||||
|
||||
|
||||
// Existing key (update)
|
||||
match map.entry(2) {
|
||||
Vacant(_) => unreachable!(),
|
||||
Occupied(mut view) => {
|
||||
let v = view.get_mut();
|
||||
*v *= 10;
|
||||
}
|
||||
}
|
||||
assert_eq!(map.get(&2).unwrap(), &200);
|
||||
assert_eq!(map.len(), 6);
|
||||
|
||||
// Existing key (take)
|
||||
match map.entry(3) {
|
||||
Vacant(_) => unreachable!(),
|
||||
Occupied(view) => {
|
||||
assert_eq!(view.remove(), 30);
|
||||
}
|
||||
}
|
||||
assert_eq!(map.get(&3), None);
|
||||
assert_eq!(map.len(), 5);
|
||||
|
||||
|
||||
// Inexistent key (insert)
|
||||
match map.entry(10) {
|
||||
Occupied(_) => unreachable!(),
|
||||
Vacant(view) => {
|
||||
assert_eq!(*view.insert(1000), 1000);
|
||||
}
|
||||
}
|
||||
assert_eq!(map.get(&10).unwrap(), &1000);
|
||||
assert_eq!(map.len(), 6);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
|
|
@ -51,7 +51,7 @@
|
|||
//! use std::error::FromError;
|
||||
//! use std::old_io::{File, IoError};
|
||||
//! use std::os::{MemoryMap, MapError};
|
||||
//! use std::path::Path;
|
||||
//! use std::old_path::Path;
|
||||
//!
|
||||
//! enum MyError {
|
||||
//! Io(IoError),
|
||||
|
|
|
@ -264,6 +264,7 @@ pub trait Show {
|
|||
#[lang = "debug_trait"]
|
||||
pub trait Debug {
|
||||
/// Formats the value using the given formatter.
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
fn fmt(&self, &mut Formatter) -> Result;
|
||||
}
|
||||
|
||||
|
@ -290,6 +291,7 @@ pub trait String {
|
|||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
pub trait Display {
|
||||
/// Formats the value using the given formatter.
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
fn fmt(&self, &mut Formatter) -> Result;
|
||||
}
|
||||
|
||||
|
|
|
@ -101,16 +101,11 @@ pub trait Iterator {
|
|||
fn size_hint(&self) -> (usize, Option<usize>) { (0, None) }
|
||||
}
|
||||
|
||||
impl<'a, T> Iterator for &'a mut (Iterator<Item=T> + 'a) {
|
||||
type Item = T;
|
||||
|
||||
fn next(&mut self) -> Option<T> {
|
||||
(**self).next()
|
||||
}
|
||||
|
||||
fn size_hint(&self) -> (usize, Option<usize>) {
|
||||
(**self).size_hint()
|
||||
}
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
impl<'a, I: Iterator + ?Sized> Iterator for &'a mut I {
|
||||
type Item = I::Item;
|
||||
fn next(&mut self) -> Option<I::Item> { (**self).next() }
|
||||
fn size_hint(&self) -> (usize, Option<usize>) { (**self).size_hint() }
|
||||
}
|
||||
|
||||
/// Conversion from an `Iterator`
|
||||
|
@ -119,6 +114,7 @@ impl<'a, T> Iterator for &'a mut (Iterator<Item=T> + 'a) {
|
|||
built from an iterator over elements of type `{A}`"]
|
||||
pub trait FromIterator<A> {
|
||||
/// Build a container with elements from an external iterator.
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
fn from_iter<T: Iterator<Item=A>>(iterator: T) -> Self;
|
||||
}
|
||||
|
||||
|
@ -548,9 +544,7 @@ pub trait IteratorExt: Iterator + Sized {
|
|||
/// assert!(it.next() == Some(5));
|
||||
/// ```
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
fn by_ref<'r>(&'r mut self) -> ByRef<'r, Self> {
|
||||
ByRef{iter: self}
|
||||
}
|
||||
fn by_ref(&mut self) -> &mut Self { self }
|
||||
|
||||
/// Loops through the entire iterator, collecting all of the elements into
|
||||
/// a container implementing `FromIterator`.
|
||||
|
@ -723,11 +717,12 @@ pub trait IteratorExt: Iterator + Sized {
|
|||
P: FnMut(Self::Item) -> bool,
|
||||
Self: ExactSizeIterator + DoubleEndedIterator
|
||||
{
|
||||
let len = self.len();
|
||||
for i in (0..len).rev() {
|
||||
if predicate(self.next_back().expect("rposition: incorrect ExactSizeIterator")) {
|
||||
let mut i = self.len() - 1;
|
||||
while let Some(v) = self.next_back() {
|
||||
if predicate(v) {
|
||||
return Some(i);
|
||||
}
|
||||
i -= 1;
|
||||
}
|
||||
None
|
||||
}
|
||||
|
@ -1017,15 +1012,22 @@ impl<I> IteratorExt for I where I: Iterator {}
|
|||
|
||||
/// A range iterator able to yield elements from both ends
|
||||
///
|
||||
/// A `DoubleEndedIterator` can be thought of as a deque in that `next()` and `next_back()` exhaust
|
||||
/// elements from the *same* range, and do not work independently of each other.
|
||||
/// A `DoubleEndedIterator` can be thought of as a deque in that `next()` and
|
||||
/// `next_back()` exhaust elements from the *same* range, and do not work
|
||||
/// independently of each other.
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
pub trait DoubleEndedIterator: Iterator {
|
||||
/// Yield an element from the end of the range, returning `None` if the range is empty.
|
||||
/// Yield an element from the end of the range, returning `None` if the
|
||||
/// range is empty.
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
fn next_back(&mut self) -> Option<Self::Item>;
|
||||
}
|
||||
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
impl<'a, I: DoubleEndedIterator + ?Sized> DoubleEndedIterator for &'a mut I {
|
||||
fn next_back(&mut self) -> Option<I::Item> { (**self).next_back() }
|
||||
}
|
||||
|
||||
/// An object implementing random access indexing by `usize`
|
||||
///
|
||||
/// A `RandomAccessIterator` should be either infinite or a `DoubleEndedIterator`.
|
||||
|
@ -1065,6 +1067,9 @@ pub trait ExactSizeIterator: Iterator {
|
|||
}
|
||||
}
|
||||
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
impl<'a, I: ExactSizeIterator + ?Sized> ExactSizeIterator for &'a mut I {}
|
||||
|
||||
// All adaptors that preserve the size of the wrapped iterator are fine
|
||||
// Adaptors that may overflow in `size_hint` are not, i.e. `Chain`.
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
|
@ -1117,32 +1122,6 @@ impl<I> RandomAccessIterator for Rev<I> where I: DoubleEndedIterator + RandomAcc
|
|||
}
|
||||
}
|
||||
|
||||
/// A mutable reference to an iterator
|
||||
#[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
pub struct ByRef<'a, I:'a> {
|
||||
iter: &'a mut I,
|
||||
}
|
||||
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
impl<'a, I> Iterator for ByRef<'a, I> where I: 'a + Iterator {
|
||||
type Item = <I as Iterator>::Item;
|
||||
|
||||
#[inline]
|
||||
fn next(&mut self) -> Option<<I as Iterator>::Item> { self.iter.next() }
|
||||
#[inline]
|
||||
fn size_hint(&self) -> (usize, Option<usize>) { self.iter.size_hint() }
|
||||
}
|
||||
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
impl<'a, I> DoubleEndedIterator for ByRef<'a, I> where I: 'a + DoubleEndedIterator {
|
||||
#[inline]
|
||||
fn next_back(&mut self) -> Option<<I as Iterator>::Item> { self.iter.next_back() }
|
||||
}
|
||||
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
impl<'a, I> ExactSizeIterator for ByRef<'a, I> where I: 'a + ExactSizeIterator {}
|
||||
|
||||
/// A trait for iterators over elements which can be added together
|
||||
#[unstable(feature = "core",
|
||||
reason = "needs to be re-evaluated as part of numerics reform")]
|
||||
|
@ -1821,6 +1800,7 @@ impl<I: Iterator> Peekable<I> {
|
|||
/// Return a reference to the next element of the iterator with out
|
||||
/// advancing it, or None if the iterator is exhausted.
|
||||
#[inline]
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
pub fn peek(&mut self) -> Option<&I::Item> {
|
||||
if self.peeked.is_none() {
|
||||
self.peeked = self.iter.next();
|
||||
|
|
|
@ -17,7 +17,7 @@ use std::cmp::Ordering;
|
|||
use std::default::Default;
|
||||
use std::fmt;
|
||||
use std::iter::FromIterator;
|
||||
use std::path::BytesContainer;
|
||||
use std::old_path::BytesContainer;
|
||||
use std::slice;
|
||||
|
||||
// Note 1: It is not clear whether the flexibility of providing both
|
||||
|
|
|
@ -45,7 +45,7 @@ impl<'a, 'v> Visitor<'v> for CheckLoopVisitor<'a> {
|
|||
ast::ExprLoop(ref b, _) => {
|
||||
self.with_context(Loop, |v| v.visit_block(&**b));
|
||||
}
|
||||
ast::ExprClosure(_, _, _, ref b) => {
|
||||
ast::ExprClosure(_, _, ref b) => {
|
||||
self.with_context(Closure, |v| v.visit_block(&**b));
|
||||
}
|
||||
ast::ExprBreak(_) => self.require_loop("break", e.span),
|
||||
|
|
|
@ -959,7 +959,7 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> {
|
|||
self.propagate_through_expr(&**e, succ)
|
||||
}
|
||||
|
||||
ast::ExprClosure(_, _, _, ref blk) => {
|
||||
ast::ExprClosure(_, _, ref blk) => {
|
||||
debug!("{} is an ExprClosure",
|
||||
expr_to_string(expr));
|
||||
|
||||
|
|
|
@ -739,7 +739,7 @@ impl<'t,'tcx,TYPER:Typer<'tcx>> MemCategorizationContext<'t,TYPER> {
|
|||
};
|
||||
|
||||
match fn_expr.node {
|
||||
ast::ExprClosure(_, _, _, ref body) => body.id,
|
||||
ast::ExprClosure(_, _, ref body) => body.id,
|
||||
_ => unreachable!()
|
||||
}
|
||||
};
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
use session::Session;
|
||||
use lint;
|
||||
use middle::ty;
|
||||
use middle::privacy::PublicItems;
|
||||
use metadata::csearch;
|
||||
use syntax::parse::token::InternedString;
|
||||
use syntax::codemap::{Span, DUMMY_SP};
|
||||
|
@ -44,15 +45,16 @@ pub struct Index {
|
|||
// A private tree-walker for producing an Index.
|
||||
struct Annotator<'a> {
|
||||
sess: &'a Session,
|
||||
index: Index,
|
||||
parent: Option<Stability>
|
||||
index: &'a mut Index,
|
||||
parent: Option<Stability>,
|
||||
export_map: &'a PublicItems,
|
||||
}
|
||||
|
||||
impl<'a> Annotator<'a> {
|
||||
// Determine the stability for a node based on its attributes and inherited
|
||||
// stability. The stability is recorded in the index and used as the parent.
|
||||
fn annotate<F>(&mut self, id: NodeId, use_parent: bool,
|
||||
attrs: &Vec<Attribute>, item_sp: Span, f: F) where
|
||||
attrs: &Vec<Attribute>, item_sp: Span, f: F, required: bool) where
|
||||
F: FnOnce(&mut Annotator),
|
||||
{
|
||||
match attr::find_stability(self.sess.diagnostic(), attrs.as_slice(), item_sp) {
|
||||
|
@ -70,7 +72,14 @@ impl<'a> Annotator<'a> {
|
|||
}
|
||||
None => {
|
||||
if use_parent {
|
||||
self.parent.clone().map(|stab| self.index.local.insert(id, stab));
|
||||
if let Some(stab) = self.parent.clone() {
|
||||
self.index.local.insert(id, stab);
|
||||
} else if self.index.staged_api && required
|
||||
&& self.export_map.contains(&id)
|
||||
&& !self.sess.opts.test {
|
||||
self.sess.span_err(item_sp,
|
||||
"This node does not have a stability attribute");
|
||||
}
|
||||
}
|
||||
f(self);
|
||||
}
|
||||
|
@ -93,11 +102,19 @@ impl<'a, 'v> Visitor<'v> for Annotator<'a> {
|
|||
_ => true,
|
||||
};
|
||||
|
||||
self.annotate(i.id, use_parent, &i.attrs, i.span, |v| visit::walk_item(v, i));
|
||||
// In case of a `pub use <mod>;`, we should not error since the stability
|
||||
// is inherited from the module itself
|
||||
let required = match i.node {
|
||||
ast::ItemUse(_) => i.vis != ast::Public,
|
||||
_ => true
|
||||
};
|
||||
|
||||
self.annotate(i.id, use_parent, &i.attrs, i.span,
|
||||
|v| visit::walk_item(v, i), required);
|
||||
|
||||
if let ast::ItemStruct(ref sd, _) = i.node {
|
||||
sd.ctor_id.map(|id| {
|
||||
self.annotate(id, true, &i.attrs, i.span, |_| {})
|
||||
self.annotate(id, true, &i.attrs, i.span, |_| {}, true)
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -106,7 +123,7 @@ impl<'a, 'v> Visitor<'v> for Annotator<'a> {
|
|||
_: &'v Block, sp: Span, _: NodeId) {
|
||||
if let FkMethod(_, _, meth) = fk {
|
||||
// Methods are not already annotated, so we annotate it
|
||||
self.annotate(meth.id, true, &meth.attrs, sp, |_| {});
|
||||
self.annotate(meth.id, true, &meth.attrs, sp, |_| {}, true);
|
||||
}
|
||||
// Items defined in a function body have no reason to have
|
||||
// a stability attribute, so we don't recurse.
|
||||
|
@ -126,27 +143,41 @@ impl<'a, 'v> Visitor<'v> for Annotator<'a> {
|
|||
TypeTraitItem(ref typedef) => (typedef.ty_param.id, &typedef.attrs,
|
||||
typedef.ty_param.span),
|
||||
};
|
||||
self.annotate(id, true, attrs, sp, |v| visit::walk_trait_item(v, t));
|
||||
self.annotate(id, true, attrs, sp, |v| visit::walk_trait_item(v, t), true);
|
||||
}
|
||||
|
||||
fn visit_variant(&mut self, var: &Variant, g: &'v Generics) {
|
||||
self.annotate(var.node.id, true, &var.node.attrs, var.span,
|
||||
|v| visit::walk_variant(v, var, g))
|
||||
|v| visit::walk_variant(v, var, g), true)
|
||||
}
|
||||
|
||||
fn visit_struct_field(&mut self, s: &StructField) {
|
||||
self.annotate(s.node.id, true, &s.node.attrs, s.span,
|
||||
|v| visit::walk_struct_field(v, s));
|
||||
|v| visit::walk_struct_field(v, s), true);
|
||||
}
|
||||
|
||||
fn visit_foreign_item(&mut self, i: &ast::ForeignItem) {
|
||||
self.annotate(i.id, true, &i.attrs, i.span, |_| {});
|
||||
self.annotate(i.id, true, &i.attrs, i.span, |_| {}, true);
|
||||
}
|
||||
}
|
||||
|
||||
impl Index {
|
||||
/// Construct the stability index for a crate being compiled.
|
||||
pub fn build(sess: &Session, krate: &Crate) -> Index {
|
||||
pub fn build(&mut self, sess: &Session, krate: &Crate, export_map: &PublicItems) {
|
||||
if !self.staged_api {
|
||||
return;
|
||||
}
|
||||
let mut annotator = Annotator {
|
||||
sess: sess,
|
||||
index: self,
|
||||
parent: None,
|
||||
export_map: export_map,
|
||||
};
|
||||
annotator.annotate(ast::CRATE_NODE_ID, true, &krate.attrs, krate.span,
|
||||
|v| visit::walk_crate(v, krate), true);
|
||||
}
|
||||
|
||||
pub fn new(krate: &Crate) -> Index {
|
||||
let mut staged_api = false;
|
||||
for attr in &krate.attrs {
|
||||
if attr.name().get() == "staged_api" {
|
||||
|
@ -159,22 +190,11 @@ impl Index {
|
|||
}
|
||||
}
|
||||
}
|
||||
let index = Index {
|
||||
Index {
|
||||
staged_api: staged_api,
|
||||
local: NodeMap(),
|
||||
extern_cache: DefIdMap()
|
||||
};
|
||||
if !staged_api {
|
||||
return index;
|
||||
}
|
||||
let mut annotator = Annotator {
|
||||
sess: sess,
|
||||
index: index,
|
||||
parent: None
|
||||
};
|
||||
annotator.annotate(ast::CRATE_NODE_ID, true, &krate.attrs, krate.span,
|
||||
|v| visit::walk_crate(v, krate));
|
||||
annotator.index
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -234,10 +254,19 @@ impl<'a, 'tcx> Checker<'a, 'tcx> {
|
|||
None => {
|
||||
// This is an 'unmarked' API, which should not exist
|
||||
// in the standard library.
|
||||
self.tcx.sess.span_err(span, "use of unmarked library feature");
|
||||
self.tcx.sess.span_note(span, "this is either a bug in the library you are \
|
||||
using or a bug in the compiler - there is \
|
||||
no way to use this feature");
|
||||
if self.tcx.sess.features.borrow().unmarked_api {
|
||||
self.tcx.sess.span_warn(span, "use of unmarked library feature");
|
||||
self.tcx.sess.span_note(span, "this is either a bug in the library you are \
|
||||
using and a bug in the compiler - please \
|
||||
report it in both places");
|
||||
} else {
|
||||
self.tcx.sess.span_err(span, "use of unmarked library feature");
|
||||
self.tcx.sess.span_note(span, "this is either a bug in the library you are \
|
||||
using and a bug in the compiler - please \
|
||||
report it in both places");
|
||||
self.tcx.sess.span_note(span, "use #![feature(unmarked_api)] in the \
|
||||
crate attributes to override this");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -80,37 +80,23 @@ pub fn poly_project_and_unify_type<'cx,'tcx>(
|
|||
obligation.repr(selcx.tcx()));
|
||||
|
||||
let infcx = selcx.infcx();
|
||||
let result = infcx.try(|snapshot| {
|
||||
infcx.try(|snapshot| {
|
||||
let (skol_predicate, skol_map) =
|
||||
infcx.skolemize_late_bound_regions(&obligation.predicate, snapshot);
|
||||
|
||||
let skol_obligation = obligation.with(skol_predicate);
|
||||
match project_and_unify_type(selcx, &skol_obligation) {
|
||||
Ok(Some(obligations)) => {
|
||||
Ok(result) => {
|
||||
match infcx.leak_check(&skol_map, snapshot) {
|
||||
Ok(()) => Ok(infcx.plug_leaks(skol_map, snapshot, &obligations)),
|
||||
Err(e) => Err(Some(MismatchedProjectionTypes { err: e })),
|
||||
Ok(()) => Ok(infcx.plug_leaks(skol_map, snapshot, &result)),
|
||||
Err(e) => Err(MismatchedProjectionTypes { err: e }),
|
||||
}
|
||||
}
|
||||
Ok(None) => {
|
||||
// Signal ambiguity using Err just so that infcx.try()
|
||||
// rolls back the snapshot. We adapt below.
|
||||
Err(None)
|
||||
}
|
||||
Err(e) => {
|
||||
Err(Some(e))
|
||||
Err(e)
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Above, we use Err(None) to signal ambiguity so that the
|
||||
// snapshot will be rolled back. But here, we want to translate to
|
||||
// Ok(None). Kind of weird.
|
||||
match result {
|
||||
Ok(obligations) => Ok(Some(obligations)),
|
||||
Err(None) => Ok(None),
|
||||
Err(Some(e)) => Err(e),
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/// Evaluates constraints of the form:
|
||||
|
@ -132,7 +118,10 @@ fn project_and_unify_type<'cx,'tcx>(
|
|||
obligation.cause.clone(),
|
||||
obligation.recursion_depth) {
|
||||
Some(n) => n,
|
||||
None => { return Ok(None); }
|
||||
None => {
|
||||
consider_unification_despite_ambiguity(selcx, obligation);
|
||||
return Ok(None);
|
||||
}
|
||||
};
|
||||
|
||||
debug!("project_and_unify_type: normalized_ty={} obligations={}",
|
||||
|
@ -147,6 +136,50 @@ fn project_and_unify_type<'cx,'tcx>(
|
|||
}
|
||||
}
|
||||
|
||||
fn consider_unification_despite_ambiguity<'cx,'tcx>(selcx: &mut SelectionContext<'cx,'tcx>,
|
||||
obligation: &ProjectionObligation<'tcx>) {
|
||||
debug!("consider_unification_despite_ambiguity(obligation={})",
|
||||
obligation.repr(selcx.tcx()));
|
||||
|
||||
let def_id = obligation.predicate.projection_ty.trait_ref.def_id;
|
||||
match selcx.tcx().lang_items.fn_trait_kind(def_id) {
|
||||
Some(_) => { }
|
||||
None => { return; }
|
||||
}
|
||||
|
||||
let infcx = selcx.infcx();
|
||||
let self_ty = obligation.predicate.projection_ty.trait_ref.self_ty();
|
||||
let self_ty = infcx.shallow_resolve(self_ty);
|
||||
debug!("consider_unification_despite_ambiguity: self_ty.sty={:?}",
|
||||
self_ty.sty);
|
||||
match self_ty.sty {
|
||||
ty::ty_closure(closure_def_id, _, substs) => {
|
||||
let closure_typer = selcx.closure_typer();
|
||||
let closure_type = closure_typer.closure_type(closure_def_id, substs);
|
||||
let ty::Binder((_, ret_type)) =
|
||||
util::closure_trait_ref_and_return_type(infcx.tcx,
|
||||
def_id,
|
||||
self_ty,
|
||||
&closure_type.sig,
|
||||
util::TupleArgumentsFlag::No);
|
||||
let (ret_type, _) =
|
||||
infcx.replace_late_bound_regions_with_fresh_var(
|
||||
obligation.cause.span,
|
||||
infer::AssocTypeProjection(obligation.predicate.projection_ty.item_name),
|
||||
&ty::Binder(ret_type));
|
||||
debug!("consider_unification_despite_ambiguity: ret_type={:?}",
|
||||
ret_type.repr(selcx.tcx()));
|
||||
let origin = infer::RelateOutputImplTypes(obligation.cause.span);
|
||||
let obligation_ty = obligation.predicate.ty;
|
||||
match infer::mk_eqty(infcx, true, origin, obligation_ty, ret_type) {
|
||||
Ok(()) => { }
|
||||
Err(_) => { /* ignore errors */ }
|
||||
}
|
||||
}
|
||||
_ => { }
|
||||
}
|
||||
}
|
||||
|
||||
/// Normalizes any associated type projections in `value`, replacing
|
||||
/// them with a fully resolved type where possible. The return value
|
||||
/// combines the normalized result and any additional obligations that
|
||||
|
|
|
@ -233,9 +233,8 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
|
|||
// is `Vec<Foo>:Iterable<Bar>`, but the impl specifies
|
||||
// `impl<T> Iterable<T> for Vec<T>`, than an error would result.
|
||||
|
||||
/// Evaluates whether the obligation can be satisfied. Returns an indication of whether the
|
||||
/// obligation can be satisfied and, if so, by what means. Never affects surrounding typing
|
||||
/// environment.
|
||||
/// Attempts to satisfy the obligation. If successful, this will affect the surrounding
|
||||
/// type environment by performing unification.
|
||||
pub fn select(&mut self, obligation: &TraitObligation<'tcx>)
|
||||
-> SelectionResult<'tcx, Selection<'tcx>> {
|
||||
debug!("select({})", obligation.repr(self.tcx()));
|
||||
|
@ -243,11 +242,68 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
|
|||
|
||||
let stack = self.push_stack(None, obligation);
|
||||
match try!(self.candidate_from_obligation(&stack)) {
|
||||
None => Ok(None),
|
||||
None => {
|
||||
self.consider_unification_despite_ambiguity(obligation);
|
||||
Ok(None)
|
||||
}
|
||||
Some(candidate) => Ok(Some(try!(self.confirm_candidate(obligation, candidate)))),
|
||||
}
|
||||
}
|
||||
|
||||
/// In the particular case of unboxed closure obligations, we can
|
||||
/// sometimes do some amount of unification for the
|
||||
/// argument/return types even though we can't yet fully match obligation.
|
||||
/// The particular case we are interesting in is an obligation of the form:
|
||||
///
|
||||
/// C : FnFoo<A>
|
||||
///
|
||||
/// where `C` is an unboxed closure type and `FnFoo` is one of the
|
||||
/// `Fn` traits. Because we know that users cannot write impls for closure types
|
||||
/// themselves, the only way that `C : FnFoo` can fail to match is under two
|
||||
/// conditions:
|
||||
///
|
||||
/// 1. The closure kind for `C` is not yet known, because inference isn't complete.
|
||||
/// 2. The closure kind for `C` *is* known, but doesn't match what is needed.
|
||||
/// For example, `C` may be a `FnOnce` closure, but a `Fn` closure is needed.
|
||||
///
|
||||
/// In either case, we always know what argument types are
|
||||
/// expected by `C`, no matter what kind of `Fn` trait it
|
||||
/// eventually matches. So we can go ahead and unify the argument
|
||||
/// types, even though the end result is ambiguous.
|
||||
///
|
||||
/// Note that this is safe *even if* the trait would never be
|
||||
/// matched (case 2 above). After all, in that case, an error will
|
||||
/// result, so it kind of doesn't matter what we do --- unifying
|
||||
/// the argument types can only be helpful to the user, because
|
||||
/// once they patch up the kind of closure that is expected, the
|
||||
/// argment types won't really change.
|
||||
fn consider_unification_despite_ambiguity(&mut self, obligation: &TraitObligation<'tcx>) {
|
||||
// Is this a `C : FnFoo(...)` trait reference for some trait binding `FnFoo`?
|
||||
match self.tcx().lang_items.fn_trait_kind(obligation.predicate.0.def_id()) {
|
||||
Some(_) => { }
|
||||
None => { return; }
|
||||
}
|
||||
|
||||
// Is the self-type a closure type? We ignore bindings here
|
||||
// because if it is a closure type, it must be a closure type from
|
||||
// within this current fn, and hence none of the higher-ranked
|
||||
// lifetimes can appear inside the self-type.
|
||||
let self_ty = self.infcx.shallow_resolve(obligation.self_ty());
|
||||
let (closure_def_id, substs) = match self_ty.sty {
|
||||
ty::ty_closure(id, _, ref substs) => (id, substs.clone()),
|
||||
_ => { return; }
|
||||
};
|
||||
assert!(!substs.has_escaping_regions());
|
||||
|
||||
let closure_trait_ref = self.closure_trait_ref(obligation, closure_def_id, substs);
|
||||
match self.confirm_poly_trait_refs(obligation.cause.clone(),
|
||||
obligation.predicate.to_poly_trait_ref(),
|
||||
closure_trait_ref) {
|
||||
Ok(()) => { }
|
||||
Err(_) => { /* Silently ignore errors. */ }
|
||||
}
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// EVALUATION
|
||||
//
|
||||
|
@ -1003,7 +1059,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
|
|||
candidates: &mut SelectionCandidateSet<'tcx>)
|
||||
-> Result<(),SelectionError<'tcx>>
|
||||
{
|
||||
let kind = match self.fn_family_trait_kind(obligation.predicate.0.def_id()) {
|
||||
let kind = match self.tcx().lang_items.fn_trait_kind(obligation.predicate.0.def_id()) {
|
||||
Some(k) => k,
|
||||
None => { return Ok(()); }
|
||||
};
|
||||
|
@ -2303,22 +2359,6 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
|
|||
impl_obligations
|
||||
}
|
||||
|
||||
fn fn_family_trait_kind(&self,
|
||||
trait_def_id: ast::DefId)
|
||||
-> Option<ty::ClosureKind>
|
||||
{
|
||||
let tcx = self.tcx();
|
||||
if Some(trait_def_id) == tcx.lang_items.fn_trait() {
|
||||
Some(ty::FnClosureKind)
|
||||
} else if Some(trait_def_id) == tcx.lang_items.fn_mut_trait() {
|
||||
Some(ty::FnMutClosureKind)
|
||||
} else if Some(trait_def_id) == tcx.lang_items.fn_once_trait() {
|
||||
Some(ty::FnOnceClosureKind)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(unused_comparisons)]
|
||||
fn derived_cause(&self,
|
||||
obligation: &TraitObligation<'tcx>,
|
||||
|
|
|
@ -306,7 +306,7 @@ impl Target {
|
|||
use std::env;
|
||||
use std::ffi::OsString;
|
||||
use std::old_io::File;
|
||||
use std::path::Path;
|
||||
use std::old_path::Path;
|
||||
use serialize::json;
|
||||
|
||||
fn load_file(path: &Path) -> Result<Target, String> {
|
||||
|
|
|
@ -324,7 +324,7 @@ pub fn closure_to_block(closure_id: ast::NodeId,
|
|||
tcx: &ty::ctxt) -> ast::NodeId {
|
||||
match tcx.map.get(closure_id) {
|
||||
ast_map::NodeExpr(expr) => match expr.node {
|
||||
ast::ExprClosure(_, _, _, ref block) => {
|
||||
ast::ExprClosure(_, _, ref block) => {
|
||||
block.id
|
||||
}
|
||||
_ => {
|
||||
|
|
|
@ -594,9 +594,6 @@ pub fn phase_3_run_analysis_passes<'tcx>(sess: Session,
|
|||
time(time_passes, "loop checking", (), |_|
|
||||
middle::check_loop::check_crate(&sess, krate));
|
||||
|
||||
let stability_index = time(time_passes, "stability index", (), |_|
|
||||
stability::Index::build(&sess, krate));
|
||||
|
||||
time(time_passes, "static item recursion checking", (), |_|
|
||||
middle::check_static_recursion::check_crate(&sess, krate, &def_map, &ast_map));
|
||||
|
||||
|
@ -608,7 +605,7 @@ pub fn phase_3_run_analysis_passes<'tcx>(sess: Session,
|
|||
freevars,
|
||||
region_map,
|
||||
lang_items,
|
||||
stability_index);
|
||||
stability::Index::new(krate));
|
||||
|
||||
// passes are timed inside typeck
|
||||
typeck::check_crate(&ty_cx, trait_map);
|
||||
|
@ -628,6 +625,10 @@ pub fn phase_3_run_analysis_passes<'tcx>(sess: Session,
|
|||
time(time_passes, "privacy checking", maps, |(a, b)|
|
||||
rustc_privacy::check_crate(&ty_cx, &export_map, a, b));
|
||||
|
||||
// Do not move this check past lint
|
||||
time(time_passes, "stability index", (), |_|
|
||||
ty_cx.stability.borrow_mut().build(&ty_cx.sess, krate, &public_items));
|
||||
|
||||
time(time_passes, "intrinsic checking", (), |_|
|
||||
middle::intrinsicck::check_crate(&ty_cx));
|
||||
|
||||
|
|
|
@ -125,7 +125,6 @@ fn test_env<F>(source_string: &str,
|
|||
resolve::resolve_crate(&sess, &ast_map, &lang_items, krate, resolve::MakeGlobMap::No);
|
||||
let named_region_map = resolve_lifetime::krate(&sess, krate, &def_map);
|
||||
let region_map = region::resolve_crate(&sess, krate);
|
||||
let stability_index = stability::Index::build(&sess, krate);
|
||||
let tcx = ty::mk_ctxt(sess,
|
||||
&arenas,
|
||||
def_map,
|
||||
|
@ -134,7 +133,7 @@ fn test_env<F>(source_string: &str,
|
|||
freevars,
|
||||
region_map,
|
||||
lang_items,
|
||||
stability_index);
|
||||
stability::Index::new(krate));
|
||||
let infcx = infer::new_infer_ctxt(&tcx);
|
||||
body(Env { infcx: &infcx });
|
||||
infcx.resolve_regions_and_report_errors(ast::CRATE_NODE_ID);
|
||||
|
|
|
@ -4521,7 +4521,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
|
|||
visit::walk_expr(self, expr);
|
||||
}
|
||||
|
||||
ExprClosure(_, _, ref fn_decl, ref block) => {
|
||||
ExprClosure(_, ref fn_decl, ref block) => {
|
||||
self.resolve_function(ClosureRibKind(expr.id),
|
||||
Some(&**fn_decl), NoTypeParameters,
|
||||
&**block);
|
||||
|
|
|
@ -1394,7 +1394,7 @@ impl<'l, 'tcx, 'v> Visitor<'v> for DxrVisitor<'l, 'tcx> {
|
|||
type, found {:?}", ty)[]),
|
||||
}
|
||||
},
|
||||
ast::ExprClosure(_, _, ref decl, ref body) => {
|
||||
ast::ExprClosure(_, ref decl, ref body) => {
|
||||
if generated_code(body.span) {
|
||||
return
|
||||
}
|
||||
|
|
|
@ -1340,7 +1340,7 @@ fn build_cfg(tcx: &ty::ctxt, id: ast::NodeId) -> (ast::NodeId, Option<cfg::CFG>)
|
|||
}
|
||||
Some(ast_map::NodeExpr(e)) => {
|
||||
match e.node {
|
||||
ast::ExprClosure(_, _, _, ref blk) => {
|
||||
ast::ExprClosure(_, _, ref blk) => {
|
||||
blk
|
||||
}
|
||||
_ => tcx.sess.bug("unexpected expr variant in has_nested_returns")
|
||||
|
|
|
@ -1283,7 +1283,7 @@ pub fn create_function_debug_context<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
|
|||
}
|
||||
ast_map::NodeExpr(ref expr) => {
|
||||
match expr.node {
|
||||
ast::ExprClosure(_, _, ref fn_decl, ref top_level_block) => {
|
||||
ast::ExprClosure(_, ref fn_decl, ref top_level_block) => {
|
||||
let name = format!("fn{}", token::gensym("fn"));
|
||||
let name = token::str_to_ident(&name[]);
|
||||
(name, &**fn_decl,
|
||||
|
@ -1590,7 +1590,7 @@ fn compile_unit_metadata(cx: &CrateContext) -> DIDescriptor {
|
|||
Some(ref p) if p.is_relative() => {
|
||||
// prepend "./" if necessary
|
||||
let dotdot = b"..";
|
||||
let prefix: &[u8] = &[dotdot[0], ::std::path::SEP_BYTE];
|
||||
let prefix: &[u8] = &[dotdot[0], ::std::old_path::SEP_BYTE];
|
||||
let mut path_bytes = p.as_vec().to_vec();
|
||||
|
||||
if &path_bytes[..2] != prefix &&
|
||||
|
@ -3595,7 +3595,7 @@ fn create_scope_map(cx: &CrateContext,
|
|||
})
|
||||
}
|
||||
|
||||
ast::ExprClosure(_, _, ref decl, ref block) => {
|
||||
ast::ExprClosure(_, ref decl, ref block) => {
|
||||
with_new_scope(cx,
|
||||
block.span,
|
||||
scope_stack,
|
||||
|
|
|
@ -1095,7 +1095,7 @@ fn trans_rvalue_dps_unadjusted<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
|
|||
ast::ExprVec(..) | ast::ExprRepeat(..) => {
|
||||
tvec::trans_fixed_vstore(bcx, expr, dest)
|
||||
}
|
||||
ast::ExprClosure(_, _, ref decl, ref body) => {
|
||||
ast::ExprClosure(_, ref decl, ref body) => {
|
||||
closure::trans_closure_expr(bcx, &**decl, &**body, expr.id, dest)
|
||||
}
|
||||
ast::ExprCall(ref f, ref args) => {
|
||||
|
|
|
@ -25,7 +25,6 @@ use util::ppaux::Repr;
|
|||
pub fn check_expr_closure<'a,'tcx>(fcx: &FnCtxt<'a,'tcx>,
|
||||
expr: &ast::Expr,
|
||||
_capture: ast::CaptureClause,
|
||||
opt_kind: Option<ast::ClosureKind>,
|
||||
decl: &'tcx ast::FnDecl,
|
||||
body: &'tcx ast::Block,
|
||||
expected: Expectation<'tcx>) {
|
||||
|
@ -33,38 +32,14 @@ pub fn check_expr_closure<'a,'tcx>(fcx: &FnCtxt<'a,'tcx>,
|
|||
expr.repr(fcx.tcx()),
|
||||
expected.repr(fcx.tcx()));
|
||||
|
||||
let expected_sig_and_kind = expected.to_option(fcx).and_then(|ty| {
|
||||
deduce_expectations_from_expected_type(fcx, ty)
|
||||
});
|
||||
|
||||
match opt_kind {
|
||||
None => {
|
||||
// If users didn't specify what sort of closure they want,
|
||||
// examine the expected type. For now, if we see explicit
|
||||
// evidence than an unboxed closure is desired, we'll use
|
||||
// that. Otherwise, we leave it unspecified, to be filled
|
||||
// in by upvar inference.
|
||||
match expected_sig_and_kind {
|
||||
None => { // don't have information about the kind, request explicit annotation
|
||||
check_closure(fcx, expr, None, decl, body, None);
|
||||
},
|
||||
Some((sig, kind)) => {
|
||||
check_closure(fcx, expr, Some(kind), decl, body, Some(sig));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Some(kind) => {
|
||||
let kind = match kind {
|
||||
ast::FnClosureKind => ty::FnClosureKind,
|
||||
ast::FnMutClosureKind => ty::FnMutClosureKind,
|
||||
ast::FnOnceClosureKind => ty::FnOnceClosureKind,
|
||||
};
|
||||
|
||||
let expected_sig = expected_sig_and_kind.map(|t| t.0);
|
||||
check_closure(fcx, expr, Some(kind), decl, body, expected_sig);
|
||||
}
|
||||
}
|
||||
// It's always helpful for inference if we know the kind of
|
||||
// closure sooner rather than later, so first examine the expected
|
||||
// type, and see if can glean a closure kind from there.
|
||||
let (expected_sig,expected_kind) = match expected.to_option(fcx) {
|
||||
Some(ty) => deduce_expectations_from_expected_type(fcx, ty),
|
||||
None => (None, None)
|
||||
};
|
||||
check_closure(fcx, expr, expected_kind, decl, body, expected_sig)
|
||||
}
|
||||
|
||||
fn check_closure<'a,'tcx>(fcx: &FnCtxt<'a,'tcx>,
|
||||
|
@ -133,21 +108,30 @@ fn check_closure<'a,'tcx>(fcx: &FnCtxt<'a,'tcx>,
|
|||
fn deduce_expectations_from_expected_type<'a,'tcx>(
|
||||
fcx: &FnCtxt<'a,'tcx>,
|
||||
expected_ty: Ty<'tcx>)
|
||||
-> Option<(ty::FnSig<'tcx>,ty::ClosureKind)>
|
||||
-> (Option<ty::FnSig<'tcx>>,Option<ty::ClosureKind>)
|
||||
{
|
||||
debug!("deduce_expectations_from_expected_type(expected_ty={})",
|
||||
expected_ty.repr(fcx.tcx()));
|
||||
|
||||
match expected_ty.sty {
|
||||
ty::ty_trait(ref object_type) => {
|
||||
let proj_bounds = object_type.projection_bounds_with_self_ty(fcx.tcx(),
|
||||
fcx.tcx().types.err);
|
||||
proj_bounds.iter()
|
||||
.filter_map(|pb| deduce_expectations_from_projection(fcx, pb))
|
||||
.next()
|
||||
let expectations =
|
||||
proj_bounds.iter()
|
||||
.filter_map(|pb| deduce_expectations_from_projection(fcx, pb))
|
||||
.next();
|
||||
|
||||
match expectations {
|
||||
Some((sig, kind)) => (Some(sig), Some(kind)),
|
||||
None => (None, None)
|
||||
}
|
||||
}
|
||||
ty::ty_infer(ty::TyVar(vid)) => {
|
||||
deduce_expectations_from_obligations(fcx, vid)
|
||||
}
|
||||
_ => {
|
||||
None
|
||||
(None, None)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -155,33 +139,61 @@ fn deduce_expectations_from_expected_type<'a,'tcx>(
|
|||
fn deduce_expectations_from_obligations<'a,'tcx>(
|
||||
fcx: &FnCtxt<'a,'tcx>,
|
||||
expected_vid: ty::TyVid)
|
||||
-> Option<(ty::FnSig<'tcx>, ty::ClosureKind)>
|
||||
-> (Option<ty::FnSig<'tcx>>, Option<ty::ClosureKind>)
|
||||
{
|
||||
let fulfillment_cx = fcx.inh.fulfillment_cx.borrow();
|
||||
// Here `expected_ty` is known to be a type inference variable.
|
||||
|
||||
fulfillment_cx.pending_obligations()
|
||||
.iter()
|
||||
.filter_map(|obligation| {
|
||||
match obligation.predicate {
|
||||
ty::Predicate::Projection(ref proj_predicate) => {
|
||||
let trait_ref = proj_predicate.to_poly_trait_ref();
|
||||
let self_ty = fcx.infcx().shallow_resolve(trait_ref.self_ty());
|
||||
match self_ty.sty {
|
||||
ty::ty_infer(ty::TyVar(v)) if expected_vid == v => {
|
||||
deduce_expectations_from_projection(fcx, proj_predicate)
|
||||
}
|
||||
_ => {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
None
|
||||
}
|
||||
}
|
||||
})
|
||||
.next()
|
||||
let expected_sig_and_kind =
|
||||
fulfillment_cx
|
||||
.pending_obligations()
|
||||
.iter()
|
||||
.filter_map(|obligation| {
|
||||
debug!("deduce_expectations_from_obligations: obligation.predicate={}",
|
||||
obligation.predicate.repr(fcx.tcx()));
|
||||
|
||||
match obligation.predicate {
|
||||
// Given a Projection predicate, we can potentially infer
|
||||
// the complete signature.
|
||||
ty::Predicate::Projection(ref proj_predicate) => {
|
||||
let trait_ref = proj_predicate.to_poly_trait_ref();
|
||||
self_type_matches_expected_vid(fcx, trait_ref, expected_vid)
|
||||
.and_then(|_| deduce_expectations_from_projection(fcx, proj_predicate))
|
||||
}
|
||||
_ => {
|
||||
None
|
||||
}
|
||||
}
|
||||
})
|
||||
.next();
|
||||
|
||||
match expected_sig_and_kind {
|
||||
Some((sig, kind)) => { return (Some(sig), Some(kind)); }
|
||||
None => { }
|
||||
}
|
||||
|
||||
// Even if we can't infer the full signature, we may be able to
|
||||
// infer the kind. This can occur if there is a trait-reference
|
||||
// like `F : Fn<A>`.
|
||||
let expected_kind =
|
||||
fulfillment_cx
|
||||
.pending_obligations()
|
||||
.iter()
|
||||
.filter_map(|obligation| {
|
||||
let opt_trait_ref = match obligation.predicate {
|
||||
ty::Predicate::Projection(ref data) => Some(data.to_poly_trait_ref()),
|
||||
ty::Predicate::Trait(ref data) => Some(data.to_poly_trait_ref()),
|
||||
ty::Predicate::Equate(..) => None,
|
||||
ty::Predicate::RegionOutlives(..) => None,
|
||||
ty::Predicate::TypeOutlives(..) => None,
|
||||
};
|
||||
opt_trait_ref
|
||||
.and_then(|trait_ref| self_type_matches_expected_vid(fcx, trait_ref, expected_vid))
|
||||
.and_then(|trait_ref| fcx.tcx().lang_items.fn_trait_kind(trait_ref.def_id()))
|
||||
})
|
||||
.next();
|
||||
|
||||
(None, expected_kind)
|
||||
}
|
||||
|
||||
/// Given a projection like "<F as Fn(X)>::Result == Y", we can deduce
|
||||
|
@ -229,3 +241,20 @@ fn deduce_expectations_from_projection<'a,'tcx>(
|
|||
return Some((fn_sig, kind));
|
||||
}
|
||||
|
||||
fn self_type_matches_expected_vid<'a,'tcx>(
|
||||
fcx: &FnCtxt<'a,'tcx>,
|
||||
trait_ref: ty::PolyTraitRef<'tcx>,
|
||||
expected_vid: ty::TyVid)
|
||||
-> Option<ty::PolyTraitRef<'tcx>>
|
||||
{
|
||||
let self_ty = fcx.infcx().shallow_resolve(trait_ref.self_ty());
|
||||
debug!("self_type_matches_expected_vid(trait_ref={}, self_ty={})",
|
||||
trait_ref.repr(fcx.tcx()),
|
||||
self_ty.repr(fcx.tcx()));
|
||||
match self_ty.sty {
|
||||
ty::ty_infer(ty::TyVar(v)) if expected_vid == v => Some(trait_ref),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -3736,8 +3736,8 @@ fn check_expr_with_unifier<'a, 'tcx, F>(fcx: &FnCtxt<'a, 'tcx>,
|
|||
ast::ExprMatch(ref discrim, ref arms, match_src) => {
|
||||
_match::check_match(fcx, expr, &**discrim, arms.as_slice(), expected, match_src);
|
||||
}
|
||||
ast::ExprClosure(capture, opt_kind, ref decl, ref body) => {
|
||||
closure::check_expr_closure(fcx, expr, capture, opt_kind, &**decl, &**body, expected);
|
||||
ast::ExprClosure(capture, ref decl, ref body) => {
|
||||
closure::check_expr_closure(fcx, expr, capture, &**decl, &**body, expected);
|
||||
}
|
||||
ast::ExprBlock(ref b) => {
|
||||
check_block_with_expected(fcx, &**b, expected);
|
||||
|
|
|
@ -638,7 +638,7 @@ fn visit_expr(rcx: &mut Rcx, expr: &ast::Expr) {
|
|||
visit::walk_expr(rcx, expr);
|
||||
}
|
||||
|
||||
ast::ExprClosure(_, _, _, ref body) => {
|
||||
ast::ExprClosure(_, _, ref body) => {
|
||||
check_expr_fn_block(rcx, expr, &**body);
|
||||
}
|
||||
|
||||
|
|
|
@ -83,7 +83,7 @@ struct SeedBorrowKind<'a,'tcx:'a> {
|
|||
impl<'a, 'tcx, 'v> Visitor<'v> for SeedBorrowKind<'a, 'tcx> {
|
||||
fn visit_expr(&mut self, expr: &ast::Expr) {
|
||||
match expr.node {
|
||||
ast::ExprClosure(cc, _, _, ref body) => {
|
||||
ast::ExprClosure(cc, _, ref body) => {
|
||||
self.check_closure(expr, cc, &**body);
|
||||
}
|
||||
|
||||
|
|
|
@ -118,7 +118,7 @@ impl<'cx, 'tcx, 'v> Visitor<'v> for WritebackCx<'cx, 'tcx> {
|
|||
MethodCall::expr(e.id));
|
||||
|
||||
match e.node {
|
||||
ast::ExprClosure(_, _, ref decl, _) => {
|
||||
ast::ExprClosure(_, ref decl, _) => {
|
||||
for input in &decl.inputs {
|
||||
let _ = self.visit_node_id(ResolvingExpr(e.span),
|
||||
input.id);
|
||||
|
|
|
@ -49,7 +49,7 @@ use rustc::middle::stability;
|
|||
use std::rc::Rc;
|
||||
use std::u32;
|
||||
use std::str::Str as StrTrait; // Conflicts with Str variant
|
||||
use std::path::Path as FsPath; // Conflicts with Path struct
|
||||
use std::old_path::Path as FsPath; // Conflicts with Path struct
|
||||
|
||||
use core::DocContext;
|
||||
use doctree;
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
Core encoding and decoding interfaces.
|
||||
*/
|
||||
|
||||
use std::path;
|
||||
use std::old_path;
|
||||
use std::rc::Rc;
|
||||
use std::cell::{Cell, RefCell};
|
||||
use std::sync::Arc;
|
||||
|
@ -538,29 +538,29 @@ macro_rules! tuple {
|
|||
|
||||
tuple! { T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, }
|
||||
|
||||
impl Encodable for path::posix::Path {
|
||||
impl Encodable for old_path::posix::Path {
|
||||
fn encode<S: Encoder>(&self, e: &mut S) -> Result<(), S::Error> {
|
||||
self.as_vec().encode(e)
|
||||
}
|
||||
}
|
||||
|
||||
impl Decodable for path::posix::Path {
|
||||
fn decode<D: Decoder>(d: &mut D) -> Result<path::posix::Path, D::Error> {
|
||||
impl Decodable for old_path::posix::Path {
|
||||
fn decode<D: Decoder>(d: &mut D) -> Result<old_path::posix::Path, D::Error> {
|
||||
let bytes: Vec<u8> = try!(Decodable::decode(d));
|
||||
Ok(path::posix::Path::new(bytes))
|
||||
Ok(old_path::posix::Path::new(bytes))
|
||||
}
|
||||
}
|
||||
|
||||
impl Encodable for path::windows::Path {
|
||||
impl Encodable for old_path::windows::Path {
|
||||
fn encode<S: Encoder>(&self, e: &mut S) -> Result<(), S::Error> {
|
||||
self.as_vec().encode(e)
|
||||
}
|
||||
}
|
||||
|
||||
impl Decodable for path::windows::Path {
|
||||
fn decode<D: Decoder>(d: &mut D) -> Result<path::windows::Path, D::Error> {
|
||||
impl Decodable for old_path::windows::Path {
|
||||
fn decode<D: Decoder>(d: &mut D) -> Result<old_path::windows::Path, D::Error> {
|
||||
let bytes: Vec<u8> = try!(Decodable::decode(d));
|
||||
Ok(path::windows::Path::new(bytes))
|
||||
Ok(old_path::windows::Path::new(bytes))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -57,7 +57,7 @@ pub fn current_dir() -> IoResult<Path> {
|
|||
///
|
||||
/// ```rust
|
||||
/// use std::env;
|
||||
/// use std::path::Path;
|
||||
/// use std::old_path::Path;
|
||||
///
|
||||
/// let root = Path::new("/");
|
||||
/// assert!(env::set_current_dir(&root).is_ok());
|
||||
|
|
|
@ -24,6 +24,7 @@ pub use self::os_str::OsStr;
|
|||
mod c_str;
|
||||
mod os_str;
|
||||
|
||||
// FIXME (#21670): these should be defined in the os_str module
|
||||
/// Freely convertible to an `&OsStr` slice.
|
||||
pub trait AsOsStr {
|
||||
/// Convert to an `&OsStr` slice.
|
||||
|
|
|
@ -41,7 +41,7 @@ use string::{String, CowString};
|
|||
use ops;
|
||||
use cmp;
|
||||
use hash::{Hash, Hasher, Writer};
|
||||
use path::{Path, GenericPath};
|
||||
use old_path::{Path, GenericPath};
|
||||
|
||||
use sys::os_str::{Buf, Slice};
|
||||
use sys_common::{AsInner, IntoInner, FromInner};
|
||||
|
|
676
src/libstd/io/buffered.rs
Normal file
676
src/libstd/io/buffered.rs
Normal file
|
@ -0,0 +1,676 @@
|
|||
// 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.
|
||||
//
|
||||
// ignore-lexer-test FIXME #15883
|
||||
|
||||
//! Buffering wrappers for I/O traits
|
||||
|
||||
use prelude::v1::*;
|
||||
use io::prelude::*;
|
||||
|
||||
use cmp;
|
||||
use error::Error as StdError;
|
||||
use error::FromError;
|
||||
use fmt;
|
||||
use io::{self, Cursor, DEFAULT_BUF_SIZE, Error, ErrorKind};
|
||||
use ptr;
|
||||
|
||||
/// Wraps a `Read` and buffers input from it
|
||||
///
|
||||
/// It can be excessively inefficient to work directly with a `Read` instance.
|
||||
/// For example, every call to `read` on `TcpStream` results in a system call.
|
||||
/// A `BufReader` performs large, infrequent reads on the underlying `Read`
|
||||
/// and maintains an in-memory buffer of the results.
|
||||
pub struct BufReader<R> {
|
||||
inner: R,
|
||||
buf: Cursor<Vec<u8>>,
|
||||
}
|
||||
|
||||
impl<R: Read> BufReader<R> {
|
||||
/// Creates a new `BufReader` with a default buffer capacity
|
||||
pub fn new(inner: R) -> BufReader<R> {
|
||||
BufReader::with_capacity(DEFAULT_BUF_SIZE, inner)
|
||||
}
|
||||
|
||||
/// Creates a new `BufReader` with the specified buffer capacity
|
||||
pub fn with_capacity(cap: usize, inner: R) -> BufReader<R> {
|
||||
BufReader {
|
||||
inner: inner,
|
||||
buf: Cursor::new(Vec::with_capacity(cap)),
|
||||
}
|
||||
}
|
||||
|
||||
/// Gets a reference to the underlying reader.
|
||||
pub fn get_ref<'a>(&self) -> &R { &self.inner }
|
||||
|
||||
/// Gets a mutable reference to the underlying reader.
|
||||
///
|
||||
/// # Warning
|
||||
///
|
||||
/// It is inadvisable to directly read from the underlying reader.
|
||||
pub fn get_mut(&mut self) -> &mut R { &mut self.inner }
|
||||
|
||||
/// Unwraps this `BufReader`, returning the underlying reader.
|
||||
///
|
||||
/// Note that any leftover data in the internal buffer is lost.
|
||||
pub fn into_inner(self) -> R { self.inner }
|
||||
}
|
||||
|
||||
impl<R: Read> Read for BufReader<R> {
|
||||
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
|
||||
// If we don't have any buffered data and we're doing a massive read
|
||||
// (larger than our internal buffer), bypass our internal buffer
|
||||
// entirely.
|
||||
if self.buf.get_ref().len() == self.buf.position() as usize &&
|
||||
buf.len() >= self.buf.get_ref().capacity() {
|
||||
return self.inner.read(buf);
|
||||
}
|
||||
try!(self.fill_buf());
|
||||
self.buf.read(buf)
|
||||
}
|
||||
}
|
||||
|
||||
impl<R: Read> BufRead for BufReader<R> {
|
||||
fn fill_buf(&mut self) -> io::Result<&[u8]> {
|
||||
// If we've reached the end of our internal buffer then we need to fetch
|
||||
// some more data from the underlying reader.
|
||||
if self.buf.position() as usize == self.buf.get_ref().len() {
|
||||
self.buf.set_position(0);
|
||||
let v = self.buf.get_mut();
|
||||
v.truncate(0);
|
||||
let inner = &mut self.inner;
|
||||
try!(super::with_end_to_cap(v, |b| inner.read(b)));
|
||||
}
|
||||
self.buf.fill_buf()
|
||||
}
|
||||
|
||||
fn consume(&mut self, amt: uint) {
|
||||
self.buf.consume(amt)
|
||||
}
|
||||
}
|
||||
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
impl<R> fmt::Debug for BufReader<R> where R: fmt::Debug {
|
||||
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(fmt, "BufReader {{ reader: {:?}, buffer: {}/{} }}",
|
||||
self.inner, self.buf.position(), self.buf.get_ref().len())
|
||||
}
|
||||
}
|
||||
|
||||
/// Wraps a Writer and buffers output to it
|
||||
///
|
||||
/// It can be excessively inefficient to work directly with a `Write`. For
|
||||
/// example, every call to `write` on `TcpStream` results in a system call. A
|
||||
/// `BufWriter` keeps an in memory buffer of data and writes it to the
|
||||
/// underlying `Write` in large, infrequent batches.
|
||||
///
|
||||
/// This writer will be flushed when it is dropped.
|
||||
pub struct BufWriter<W> {
|
||||
inner: Option<W>,
|
||||
buf: Vec<u8>,
|
||||
}
|
||||
|
||||
/// An error returned by `into_inner` which indicates whether a flush error
|
||||
/// happened or not.
|
||||
#[derive(Debug)]
|
||||
pub struct IntoInnerError<W>(W, Error);
|
||||
|
||||
impl<W: Write> BufWriter<W> {
|
||||
/// Creates a new `BufWriter` with a default buffer capacity
|
||||
pub fn new(inner: W) -> BufWriter<W> {
|
||||
BufWriter::with_capacity(DEFAULT_BUF_SIZE, inner)
|
||||
}
|
||||
|
||||
/// Creates a new `BufWriter` with the specified buffer capacity
|
||||
pub fn with_capacity(cap: usize, inner: W) -> BufWriter<W> {
|
||||
BufWriter {
|
||||
inner: Some(inner),
|
||||
buf: Vec::with_capacity(cap),
|
||||
}
|
||||
}
|
||||
|
||||
fn flush_buf(&mut self) -> io::Result<()> {
|
||||
let mut written = 0;
|
||||
let len = self.buf.len();
|
||||
let mut ret = Ok(());
|
||||
while written < len {
|
||||
match self.inner.as_mut().unwrap().write(&self.buf[written..]) {
|
||||
Ok(0) => {
|
||||
ret = Err(Error::new(ErrorKind::WriteZero,
|
||||
"failed to flush", None));
|
||||
break;
|
||||
}
|
||||
Ok(n) => written += n,
|
||||
Err(e) => { ret = Err(e); break }
|
||||
|
||||
}
|
||||
}
|
||||
if written > 0 {
|
||||
// NB: would be better expressed as .remove(0..n) if it existed
|
||||
unsafe {
|
||||
ptr::copy_memory(self.buf.as_mut_ptr(),
|
||||
self.buf.as_ptr().offset(written as isize),
|
||||
len - written);
|
||||
}
|
||||
}
|
||||
self.buf.truncate(len - written);
|
||||
ret
|
||||
}
|
||||
|
||||
/// Gets a reference to the underlying writer.
|
||||
pub fn get_ref(&self) -> &W { self.inner.as_ref().unwrap() }
|
||||
|
||||
/// Gets a mutable reference to the underlying write.
|
||||
///
|
||||
/// # Warning
|
||||
///
|
||||
/// It is inadvisable to directly read from the underlying writer.
|
||||
pub fn get_mut(&mut self) -> &mut W { self.inner.as_mut().unwrap() }
|
||||
|
||||
/// Unwraps this `BufWriter`, returning the underlying writer.
|
||||
///
|
||||
/// The buffer is flushed before returning the writer.
|
||||
pub fn into_inner(mut self) -> Result<W, IntoInnerError<BufWriter<W>>> {
|
||||
match self.flush_buf() {
|
||||
Err(e) => Err(IntoInnerError(self, e)),
|
||||
Ok(()) => Ok(self.inner.take().unwrap())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<W: Write> Write for BufWriter<W> {
|
||||
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
|
||||
if self.buf.len() + buf.len() > self.buf.capacity() {
|
||||
try!(self.flush_buf());
|
||||
}
|
||||
if buf.len() >= self.buf.capacity() {
|
||||
self.inner.as_mut().unwrap().write(buf)
|
||||
} else {
|
||||
let amt = cmp::min(buf.len(), self.buf.capacity());
|
||||
Write::write(&mut self.buf, &buf[..amt])
|
||||
}
|
||||
}
|
||||
fn flush(&mut self) -> io::Result<()> {
|
||||
self.flush_buf().and_then(|()| self.get_mut().flush())
|
||||
}
|
||||
}
|
||||
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
impl<W> fmt::Debug for BufWriter<W> where W: fmt::Debug {
|
||||
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(fmt, "BufWriter {{ writer: {:?}, buffer: {}/{} }}",
|
||||
self.inner.as_ref().unwrap(), self.buf.len(), self.buf.capacity())
|
||||
}
|
||||
}
|
||||
|
||||
#[unsafe_destructor]
|
||||
impl<W: Write> Drop for BufWriter<W> {
|
||||
fn drop(&mut self) {
|
||||
if self.inner.is_some() {
|
||||
// dtors should not panic, so we ignore a failed flush
|
||||
let _r = self.flush_buf();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<W> IntoInnerError<W> {
|
||||
/// Returns the error which caused the call to `into_inner` to fail.
|
||||
///
|
||||
/// This error was returned when attempting to flush the internal buffer.
|
||||
pub fn error(&self) -> &Error { &self.1 }
|
||||
|
||||
/// Returns the underlying `BufWriter` instance which generated the error.
|
||||
///
|
||||
/// The returned object can be used to retry a flush or re-inspect the
|
||||
/// buffer.
|
||||
pub fn into_inner(self) -> W { self.0 }
|
||||
}
|
||||
|
||||
impl<W> FromError<IntoInnerError<W>> for Error {
|
||||
fn from_error(iie: IntoInnerError<W>) -> Error { iie.1 }
|
||||
}
|
||||
|
||||
impl<W> StdError for IntoInnerError<W> {
|
||||
fn description(&self) -> &str { self.error().description() }
|
||||
}
|
||||
|
||||
impl<W> fmt::Display for IntoInnerError<W> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
self.error().fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
/// Wraps a Writer and buffers output to it, flushing whenever a newline
|
||||
/// (`0x0a`, `'\n'`) is detected.
|
||||
///
|
||||
/// This writer will be flushed when it is dropped.
|
||||
pub struct LineWriter<W> {
|
||||
inner: BufWriter<W>,
|
||||
}
|
||||
|
||||
impl<W: Write> LineWriter<W> {
|
||||
/// Creates a new `LineWriter`
|
||||
pub fn new(inner: W) -> LineWriter<W> {
|
||||
// Lines typically aren't that long, don't use a giant buffer
|
||||
LineWriter { inner: BufWriter::with_capacity(1024, inner) }
|
||||
}
|
||||
|
||||
/// Gets a reference to the underlying writer.
|
||||
///
|
||||
/// This type does not expose the ability to get a mutable reference to the
|
||||
/// underlying reader because that could possibly corrupt the buffer.
|
||||
pub fn get_ref<'a>(&'a self) -> &'a W { self.inner.get_ref() }
|
||||
|
||||
/// Unwraps this `LineWriter`, returning the underlying writer.
|
||||
///
|
||||
/// The internal buffer is flushed before returning the writer.
|
||||
pub fn into_inner(self) -> Result<W, IntoInnerError<LineWriter<W>>> {
|
||||
self.inner.into_inner().map_err(|IntoInnerError(buf, e)| {
|
||||
IntoInnerError(LineWriter { inner: buf }, e)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<W: Write> Write for LineWriter<W> {
|
||||
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
|
||||
match buf.rposition_elem(&b'\n') {
|
||||
Some(i) => {
|
||||
let n = try!(self.inner.write(&buf[..i + 1]));
|
||||
if n != i + 1 { return Ok(n) }
|
||||
try!(self.inner.flush());
|
||||
self.inner.write(&buf[i + 1..]).map(|i| n + i)
|
||||
}
|
||||
None => self.inner.write(buf),
|
||||
}
|
||||
}
|
||||
|
||||
fn flush(&mut self) -> io::Result<()> { self.inner.flush() }
|
||||
}
|
||||
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
impl<W> fmt::Debug for LineWriter<W> where W: fmt::Debug {
|
||||
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(fmt, "LineWriter {{ writer: {:?}, buffer: {}/{} }}",
|
||||
self.inner.inner, self.inner.buf.len(),
|
||||
self.inner.buf.capacity())
|
||||
}
|
||||
}
|
||||
|
||||
struct InternalBufWriter<W>(BufWriter<W>);
|
||||
|
||||
impl<W> InternalBufWriter<W> {
|
||||
fn get_mut(&mut self) -> &mut BufWriter<W> {
|
||||
let InternalBufWriter(ref mut w) = *self;
|
||||
return w;
|
||||
}
|
||||
}
|
||||
|
||||
impl<W: Read> Read for InternalBufWriter<W> {
|
||||
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
|
||||
self.get_mut().inner.as_mut().unwrap().read(buf)
|
||||
}
|
||||
}
|
||||
|
||||
/// Wraps a Stream and buffers input and output to and from it.
|
||||
///
|
||||
/// It can be excessively inefficient to work directly with a `Stream`. For
|
||||
/// example, every call to `read` or `write` on `TcpStream` results in a system
|
||||
/// call. A `BufStream` keeps in memory buffers of data, making large,
|
||||
/// infrequent calls to `read` and `write` on the underlying `Stream`.
|
||||
///
|
||||
/// The output half will be flushed when this stream is dropped.
|
||||
pub struct BufStream<S> {
|
||||
inner: BufReader<InternalBufWriter<S>>
|
||||
}
|
||||
|
||||
impl<S: Read + Write> BufStream<S> {
|
||||
/// Creates a new buffered stream with explicitly listed capacities for the
|
||||
/// reader/writer buffer.
|
||||
pub fn with_capacities(reader_cap: usize, writer_cap: usize, inner: S)
|
||||
-> BufStream<S> {
|
||||
let writer = BufWriter::with_capacity(writer_cap, inner);
|
||||
let internal_writer = InternalBufWriter(writer);
|
||||
let reader = BufReader::with_capacity(reader_cap, internal_writer);
|
||||
BufStream { inner: reader }
|
||||
}
|
||||
|
||||
/// Creates a new buffered stream with the default reader/writer buffer
|
||||
/// capacities.
|
||||
pub fn new(inner: S) -> BufStream<S> {
|
||||
BufStream::with_capacities(DEFAULT_BUF_SIZE, DEFAULT_BUF_SIZE, inner)
|
||||
}
|
||||
|
||||
/// Gets a reference to the underlying stream.
|
||||
pub fn get_ref(&self) -> &S {
|
||||
let InternalBufWriter(ref w) = self.inner.inner;
|
||||
w.get_ref()
|
||||
}
|
||||
|
||||
/// Gets a mutable reference to the underlying stream.
|
||||
///
|
||||
/// # Warning
|
||||
///
|
||||
/// It is inadvisable to read directly from or write directly to the
|
||||
/// underlying stream.
|
||||
pub fn get_mut(&mut self) -> &mut S {
|
||||
let InternalBufWriter(ref mut w) = self.inner.inner;
|
||||
w.get_mut()
|
||||
}
|
||||
|
||||
/// Unwraps this `BufStream`, returning the underlying stream.
|
||||
///
|
||||
/// The internal buffer is flushed before returning the stream. Any leftover
|
||||
/// data in the read buffer is lost.
|
||||
pub fn into_inner(self) -> Result<S, IntoInnerError<BufStream<S>>> {
|
||||
let BufReader { inner: InternalBufWriter(w), buf } = self.inner;
|
||||
w.into_inner().map_err(|IntoInnerError(w, e)| {
|
||||
IntoInnerError(BufStream {
|
||||
inner: BufReader { inner: InternalBufWriter(w), buf: buf },
|
||||
}, e)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<S: Read + Write> BufRead for BufStream<S> {
|
||||
fn fill_buf(&mut self) -> io::Result<&[u8]> { self.inner.fill_buf() }
|
||||
fn consume(&mut self, amt: uint) { self.inner.consume(amt) }
|
||||
}
|
||||
|
||||
impl<S: Read + Write> Read for BufStream<S> {
|
||||
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
|
||||
self.inner.read(buf)
|
||||
}
|
||||
}
|
||||
|
||||
impl<S: Read + Write> Write for BufStream<S> {
|
||||
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
|
||||
self.inner.inner.get_mut().write(buf)
|
||||
}
|
||||
fn flush(&mut self) -> io::Result<()> {
|
||||
self.inner.inner.get_mut().flush()
|
||||
}
|
||||
}
|
||||
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
impl<S> fmt::Debug for BufStream<S> where S: fmt::Debug {
|
||||
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
||||
let reader = &self.inner;
|
||||
let writer = &self.inner.inner.0;
|
||||
write!(fmt, "BufStream {{ stream: {:?}, write_buffer: {}/{}, read_buffer: {}/{} }}",
|
||||
writer.inner,
|
||||
writer.buf.len(), writer.buf.capacity(),
|
||||
reader.buf.position(), reader.buf.get_ref().len())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use prelude::v1::*;
|
||||
use io::prelude::*;
|
||||
use io::{self, BufReader, BufWriter, BufStream, Cursor, LineWriter};
|
||||
use test;
|
||||
|
||||
/// A dummy reader intended at testing short-reads propagation.
|
||||
pub struct ShortReader {
|
||||
lengths: Vec<usize>,
|
||||
}
|
||||
|
||||
impl Read for ShortReader {
|
||||
fn read(&mut self, _: &mut [u8]) -> io::Result<usize> {
|
||||
if self.lengths.is_empty() {
|
||||
Ok(0)
|
||||
} else {
|
||||
Ok(self.lengths.remove(0))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_buffered_reader() {
|
||||
let inner: &[u8] = &[5, 6, 7, 0, 1, 2, 3, 4];
|
||||
let mut reader = BufReader::with_capacity(2, inner);
|
||||
|
||||
let mut buf = [0, 0, 0];
|
||||
let nread = reader.read(&mut buf);
|
||||
assert_eq!(Ok(3), nread);
|
||||
let b: &[_] = &[5, 6, 7];
|
||||
assert_eq!(buf, b);
|
||||
|
||||
let mut buf = [0, 0];
|
||||
let nread = reader.read(&mut buf);
|
||||
assert_eq!(Ok(2), nread);
|
||||
let b: &[_] = &[0, 1];
|
||||
assert_eq!(buf, b);
|
||||
|
||||
let mut buf = [0];
|
||||
let nread = reader.read(&mut buf);
|
||||
assert_eq!(Ok(1), nread);
|
||||
let b: &[_] = &[2];
|
||||
assert_eq!(buf, b);
|
||||
|
||||
let mut buf = [0, 0, 0];
|
||||
let nread = reader.read(&mut buf);
|
||||
assert_eq!(Ok(1), nread);
|
||||
let b: &[_] = &[3, 0, 0];
|
||||
assert_eq!(buf, b);
|
||||
|
||||
let nread = reader.read(&mut buf);
|
||||
assert_eq!(Ok(1), nread);
|
||||
let b: &[_] = &[4, 0, 0];
|
||||
assert_eq!(buf, b);
|
||||
|
||||
assert_eq!(reader.read(&mut buf), Ok(0));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_buffered_writer() {
|
||||
let inner = Vec::new();
|
||||
let mut writer = BufWriter::with_capacity(2, inner);
|
||||
|
||||
writer.write(&[0, 1]).unwrap();
|
||||
assert_eq!(*writer.get_ref(), [0, 1]);
|
||||
|
||||
writer.write(&[2]).unwrap();
|
||||
assert_eq!(*writer.get_ref(), [0, 1]);
|
||||
|
||||
writer.write(&[3]).unwrap();
|
||||
assert_eq!(*writer.get_ref(), [0, 1]);
|
||||
|
||||
writer.flush().unwrap();
|
||||
assert_eq!(*writer.get_ref(), [0, 1, 2, 3]);
|
||||
|
||||
writer.write(&[4]).unwrap();
|
||||
writer.write(&[5]).unwrap();
|
||||
assert_eq!(*writer.get_ref(), [0, 1, 2, 3]);
|
||||
|
||||
writer.write(&[6]).unwrap();
|
||||
assert_eq!(*writer.get_ref(), [0, 1, 2, 3, 4, 5]);
|
||||
|
||||
writer.write(&[7, 8]).unwrap();
|
||||
assert_eq!(*writer.get_ref(), [0, 1, 2, 3, 4, 5, 6, 7, 8]);
|
||||
|
||||
writer.write(&[9, 10, 11]).unwrap();
|
||||
let a: &[_] = &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11];
|
||||
assert_eq!(*writer.get_ref(), [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]);
|
||||
|
||||
writer.flush().unwrap();
|
||||
assert_eq!(*writer.get_ref(), [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_buffered_writer_inner_flushes() {
|
||||
let mut w = BufWriter::with_capacity(3, Vec::new());
|
||||
w.write(&[0, 1]).unwrap();
|
||||
assert_eq!(*w.get_ref(), []);
|
||||
let w = w.into_inner().unwrap();
|
||||
assert_eq!(w, [0, 1]);
|
||||
}
|
||||
|
||||
// This is just here to make sure that we don't infinite loop in the
|
||||
// newtype struct autoderef weirdness
|
||||
#[test]
|
||||
fn test_buffered_stream() {
|
||||
struct S;
|
||||
|
||||
impl Write for S {
|
||||
fn write(&mut self, b: &[u8]) -> io::Result<usize> { Ok(b.len()) }
|
||||
fn flush(&mut self) -> io::Result<()> { Ok(()) }
|
||||
}
|
||||
|
||||
impl Read for S {
|
||||
fn read(&mut self, _: &mut [u8]) -> io::Result<usize> { Ok(0) }
|
||||
}
|
||||
|
||||
let mut stream = BufStream::new(S);
|
||||
assert_eq!(stream.read(&mut [0; 10]), Ok(0));
|
||||
stream.write(&[0; 10]).unwrap();
|
||||
stream.flush().unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_read_until() {
|
||||
let inner: &[u8] = &[0, 1, 2, 1, 0];
|
||||
let mut reader = BufReader::with_capacity(2, inner);
|
||||
let mut v = Vec::new();
|
||||
reader.read_until(0, &mut v).unwrap();
|
||||
assert_eq!(v, [0]);
|
||||
v.truncate(0);
|
||||
reader.read_until(2, &mut v).unwrap();
|
||||
assert_eq!(v, [1, 2]);
|
||||
v.truncate(0);
|
||||
reader.read_until(1, &mut v).unwrap();
|
||||
assert_eq!(v, [1]);
|
||||
v.truncate(0);
|
||||
reader.read_until(8, &mut v).unwrap();
|
||||
assert_eq!(v, [0]);
|
||||
v.truncate(0);
|
||||
reader.read_until(9, &mut v).unwrap();
|
||||
assert_eq!(v, []);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_line_buffer() {
|
||||
let mut writer = LineWriter::new(Vec::new());
|
||||
writer.write(&[0]).unwrap();
|
||||
assert_eq!(*writer.get_ref(), []);
|
||||
writer.write(&[1]).unwrap();
|
||||
assert_eq!(*writer.get_ref(), []);
|
||||
writer.flush().unwrap();
|
||||
assert_eq!(*writer.get_ref(), [0, 1]);
|
||||
writer.write(&[0, b'\n', 1, b'\n', 2]).unwrap();
|
||||
assert_eq!(*writer.get_ref(), [0, 1, 0, b'\n', 1, b'\n']);
|
||||
writer.flush().unwrap();
|
||||
assert_eq!(*writer.get_ref(), [0, 1, 0, b'\n', 1, b'\n', 2]);
|
||||
writer.write(&[3, b'\n']).unwrap();
|
||||
assert_eq!(*writer.get_ref(), [0, 1, 0, b'\n', 1, b'\n', 2, 3, b'\n']);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_read_line() {
|
||||
let in_buf = b"a\nb\nc";
|
||||
let mut reader = BufReader::with_capacity(2, in_buf);
|
||||
let mut s = String::new();
|
||||
reader.read_line(&mut s).unwrap();
|
||||
assert_eq!(s, "a\n");
|
||||
s.truncate(0);
|
||||
reader.read_line(&mut s).unwrap();
|
||||
assert_eq!(s, "b\n");
|
||||
s.truncate(0);
|
||||
reader.read_line(&mut s).unwrap();
|
||||
assert_eq!(s, "c");
|
||||
s.truncate(0);
|
||||
reader.read_line(&mut s).unwrap();
|
||||
assert_eq!(s, "");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_lines() {
|
||||
let in_buf = b"a\nb\nc";
|
||||
let mut reader = BufReader::with_capacity(2, in_buf);
|
||||
let mut it = reader.lines();
|
||||
assert_eq!(it.next(), Some(Ok("a".to_string())));
|
||||
assert_eq!(it.next(), Some(Ok("b".to_string())));
|
||||
assert_eq!(it.next(), Some(Ok("c".to_string())));
|
||||
assert_eq!(it.next(), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_short_reads() {
|
||||
let inner = ShortReader{lengths: vec![0, 1, 2, 0, 1, 0]};
|
||||
let mut reader = BufReader::new(inner);
|
||||
let mut buf = [0, 0];
|
||||
assert_eq!(reader.read(&mut buf), Ok(0));
|
||||
assert_eq!(reader.read(&mut buf), Ok(1));
|
||||
assert_eq!(reader.read(&mut buf), Ok(2));
|
||||
assert_eq!(reader.read(&mut buf), Ok(0));
|
||||
assert_eq!(reader.read(&mut buf), Ok(1));
|
||||
assert_eq!(reader.read(&mut buf), Ok(0));
|
||||
assert_eq!(reader.read(&mut buf), Ok(0));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn read_char_buffered() {
|
||||
let buf = [195u8, 159u8];
|
||||
let mut reader = BufReader::with_capacity(1, &buf[]);
|
||||
assert_eq!(reader.chars().next(), Some(Ok('ß')));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_chars() {
|
||||
let buf = [195u8, 159u8, b'a'];
|
||||
let mut reader = BufReader::with_capacity(1, &buf[]);
|
||||
let mut it = reader.chars();
|
||||
assert_eq!(it.next(), Some(Ok('ß')));
|
||||
assert_eq!(it.next(), Some(Ok('a')));
|
||||
assert_eq!(it.next(), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_fail]
|
||||
fn dont_panic_in_drop_on_panicked_flush() {
|
||||
struct FailFlushWriter;
|
||||
|
||||
impl Write for FailFlushWriter {
|
||||
fn write(&mut self, buf: &[u8]) -> io::Result<usize> { Ok(buf.len()) }
|
||||
fn flush(&mut self) -> io::Result<()> {
|
||||
Err(io::Error::last_os_error())
|
||||
}
|
||||
}
|
||||
|
||||
let writer = FailFlushWriter;
|
||||
let _writer = BufWriter::new(writer);
|
||||
|
||||
// If writer panics *again* due to the flush error then the process will
|
||||
// abort.
|
||||
panic!();
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn bench_buffered_reader(b: &mut test::Bencher) {
|
||||
b.iter(|| {
|
||||
BufReader::new(io::empty())
|
||||
});
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn bench_buffered_writer(b: &mut test::Bencher) {
|
||||
b.iter(|| {
|
||||
BufWriter::new(io::sink())
|
||||
});
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn bench_buffered_stream(b: &mut test::Bencher) {
|
||||
let mut buf = Cursor::new(Vec::new());
|
||||
b.iter(|| {
|
||||
BufStream::new(&mut buf);
|
||||
});
|
||||
}
|
||||
}
|
408
src/libstd/io/cursor.rs
Normal file
408
src/libstd/io/cursor.rs
Normal file
|
@ -0,0 +1,408 @@
|
|||
// Copyright 2015 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.
|
||||
|
||||
#![allow(missing_copy_implementations)]
|
||||
|
||||
use prelude::v1::*;
|
||||
use io::prelude::*;
|
||||
|
||||
use cmp;
|
||||
use io::{self, SeekFrom, Error, ErrorKind};
|
||||
use iter::repeat;
|
||||
use num::Int;
|
||||
use slice;
|
||||
|
||||
/// A `Cursor` is a type which wraps another I/O object to provide a `Seek`
|
||||
/// implementation.
|
||||
///
|
||||
/// Cursors are currently typically used with memory buffer objects in order to
|
||||
/// allow `Seek` plus `Read` and `Write` implementations. For example, common
|
||||
/// cursor types include:
|
||||
///
|
||||
/// * `Cursor<Vec<u8>>`
|
||||
/// * `Cursor<&[u8]>`
|
||||
///
|
||||
/// Implementations of the I/O traits for `Cursor<T>` are not currently generic
|
||||
/// over `T` itself. Instead, specific implementations are provided for various
|
||||
/// in-memory buffer types like `Vec<u8>` and `&[u8]`.
|
||||
pub struct Cursor<T> {
|
||||
inner: T,
|
||||
pos: u64,
|
||||
}
|
||||
|
||||
impl<T> Cursor<T> {
|
||||
/// Create a new cursor wrapping the provided underlying I/O object.
|
||||
pub fn new(inner: T) -> Cursor<T> {
|
||||
Cursor { pos: 0, inner: inner }
|
||||
}
|
||||
|
||||
/// Consume this cursor, returning the underlying value.
|
||||
pub fn into_inner(self) -> T { self.inner }
|
||||
|
||||
/// Get a reference to the underlying value in this cursor.
|
||||
pub fn get_ref(&self) -> &T { &self.inner }
|
||||
|
||||
/// Get a mutable reference to the underlying value in this cursor.
|
||||
///
|
||||
/// Care should be taken to avoid modifying the internal I/O state of the
|
||||
/// underlying value as it may corrupt this cursor's position.
|
||||
pub fn get_mut(&mut self) -> &mut T { &mut self.inner }
|
||||
|
||||
/// Returns the current value of this cursor
|
||||
pub fn position(&self) -> u64 { self.pos }
|
||||
|
||||
/// Sets the value of this cursor
|
||||
pub fn set_position(&mut self, pos: u64) { self.pos = pos; }
|
||||
}
|
||||
|
||||
macro_rules! seek {
|
||||
() => {
|
||||
fn seek(&mut self, style: SeekFrom) -> io::Result<u64> {
|
||||
let pos = match style {
|
||||
SeekFrom::Start(n) => { self.pos = n; return Ok(n) }
|
||||
SeekFrom::End(n) => self.inner.len() as i64 + n,
|
||||
SeekFrom::Current(n) => self.pos as i64 + n,
|
||||
};
|
||||
|
||||
if pos < 0 {
|
||||
Err(Error::new(ErrorKind::InvalidInput,
|
||||
"invalid seek to a negative position",
|
||||
None))
|
||||
} else {
|
||||
self.pos = pos as u64;
|
||||
Ok(self.pos)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> io::Seek for Cursor<&'a [u8]> { seek!(); }
|
||||
impl<'a> io::Seek for Cursor<&'a mut [u8]> { seek!(); }
|
||||
impl io::Seek for Cursor<Vec<u8>> { seek!(); }
|
||||
|
||||
macro_rules! read {
|
||||
() => {
|
||||
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
|
||||
let n = try!(Read::read(&mut try!(self.fill_buf()), buf));
|
||||
self.pos += n as u64;
|
||||
Ok(n)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Read for Cursor<&'a [u8]> { read!(); }
|
||||
impl<'a> Read for Cursor<&'a mut [u8]> { read!(); }
|
||||
impl Read for Cursor<Vec<u8>> { read!(); }
|
||||
|
||||
macro_rules! buffer {
|
||||
() => {
|
||||
fn fill_buf(&mut self) -> io::Result<&[u8]> {
|
||||
let amt = cmp::min(self.pos, self.inner.len() as u64);
|
||||
Ok(&self.inner[(amt as usize)..])
|
||||
}
|
||||
fn consume(&mut self, amt: usize) { self.pos += amt as u64; }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> BufRead for Cursor<&'a [u8]> { buffer!(); }
|
||||
impl<'a> BufRead for Cursor<&'a mut [u8]> { buffer!(); }
|
||||
impl<'a> BufRead for Cursor<Vec<u8>> { buffer!(); }
|
||||
|
||||
impl<'a> Write for Cursor<&'a mut [u8]> {
|
||||
fn write(&mut self, data: &[u8]) -> io::Result<usize> {
|
||||
let pos = cmp::min(self.pos, self.inner.len() as u64);
|
||||
let amt = try!((&mut self.inner[(pos as usize)..]).write(data));
|
||||
self.pos += amt as u64;
|
||||
Ok(amt)
|
||||
}
|
||||
fn flush(&mut self) -> io::Result<()> { Ok(()) }
|
||||
}
|
||||
|
||||
impl Write for Cursor<Vec<u8>> {
|
||||
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
|
||||
// Make sure the internal buffer is as least as big as where we
|
||||
// currently are
|
||||
let pos = self.position();
|
||||
let amt = pos.saturating_sub(self.inner.len() as u64);
|
||||
self.inner.extend(repeat(0).take(amt as usize));
|
||||
|
||||
// Figure out what bytes will be used to overwrite what's currently
|
||||
// there (left), and what will be appended on the end (right)
|
||||
let space = self.inner.len() - pos as usize;
|
||||
let (left, right) = buf.split_at(cmp::min(space, buf.len()));
|
||||
slice::bytes::copy_memory(&mut self.inner[(pos as usize)..], left);
|
||||
self.inner.push_all(right);
|
||||
|
||||
// Bump us forward
|
||||
self.set_position(pos + buf.len() as u64);
|
||||
Ok(buf.len())
|
||||
}
|
||||
fn flush(&mut self) -> io::Result<()> { Ok(()) }
|
||||
}
|
||||
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use core::prelude::*;
|
||||
|
||||
use io::prelude::*;
|
||||
use io::{Cursor, SeekFrom};
|
||||
use vec::Vec;
|
||||
|
||||
#[test]
|
||||
fn test_vec_writer() {
|
||||
let mut writer = Vec::new();
|
||||
assert_eq!(writer.write(&[0]), Ok(1));
|
||||
assert_eq!(writer.write(&[1, 2, 3]), Ok(3));
|
||||
assert_eq!(writer.write(&[4, 5, 6, 7]), Ok(4));
|
||||
let b: &[_] = &[0, 1, 2, 3, 4, 5, 6, 7];
|
||||
assert_eq!(writer, b);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_mem_writer() {
|
||||
let mut writer = Cursor::new(Vec::new());
|
||||
assert_eq!(writer.write(&[0]), Ok(1));
|
||||
assert_eq!(writer.write(&[1, 2, 3]), Ok(3));
|
||||
assert_eq!(writer.write(&[4, 5, 6, 7]), Ok(4));
|
||||
let b: &[_] = &[0, 1, 2, 3, 4, 5, 6, 7];
|
||||
assert_eq!(&writer.get_ref()[], b);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_buf_writer() {
|
||||
let mut buf = [0 as u8; 9];
|
||||
{
|
||||
let mut writer = Cursor::new(&mut buf[]);
|
||||
assert_eq!(writer.position(), 0);
|
||||
assert_eq!(writer.write(&[0]), Ok(1));
|
||||
assert_eq!(writer.position(), 1);
|
||||
assert_eq!(writer.write(&[1, 2, 3]), Ok(3));
|
||||
assert_eq!(writer.write(&[4, 5, 6, 7]), Ok(4));
|
||||
assert_eq!(writer.position(), 8);
|
||||
assert_eq!(writer.write(&[]), Ok(0));
|
||||
assert_eq!(writer.position(), 8);
|
||||
|
||||
assert_eq!(writer.write(&[8, 9]), Ok(1));
|
||||
assert_eq!(writer.write(&[10]), Ok(0));
|
||||
}
|
||||
let b: &[_] = &[0, 1, 2, 3, 4, 5, 6, 7, 8];
|
||||
assert_eq!(buf, b);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_buf_writer_seek() {
|
||||
let mut buf = [0 as u8; 8];
|
||||
{
|
||||
let mut writer = Cursor::new(&mut buf[]);
|
||||
assert_eq!(writer.position(), 0);
|
||||
assert_eq!(writer.write(&[1]), Ok(1));
|
||||
assert_eq!(writer.position(), 1);
|
||||
|
||||
assert_eq!(writer.seek(SeekFrom::Start(2)), Ok(2));
|
||||
assert_eq!(writer.position(), 2);
|
||||
assert_eq!(writer.write(&[2]), Ok(1));
|
||||
assert_eq!(writer.position(), 3);
|
||||
|
||||
assert_eq!(writer.seek(SeekFrom::Current(-2)), Ok(1));
|
||||
assert_eq!(writer.position(), 1);
|
||||
assert_eq!(writer.write(&[3]), Ok(1));
|
||||
assert_eq!(writer.position(), 2);
|
||||
|
||||
assert_eq!(writer.seek(SeekFrom::End(-1)), Ok(7));
|
||||
assert_eq!(writer.position(), 7);
|
||||
assert_eq!(writer.write(&[4]), Ok(1));
|
||||
assert_eq!(writer.position(), 8);
|
||||
|
||||
}
|
||||
let b: &[_] = &[1, 3, 2, 0, 0, 0, 0, 4];
|
||||
assert_eq!(buf, b);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_buf_writer_error() {
|
||||
let mut buf = [0 as u8; 2];
|
||||
let mut writer = Cursor::new(&mut buf[]);
|
||||
assert_eq!(writer.write(&[0]), Ok(1));
|
||||
assert_eq!(writer.write(&[0, 0]), Ok(1));
|
||||
assert_eq!(writer.write(&[0, 0]), Ok(0));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_mem_reader() {
|
||||
let mut reader = Cursor::new(vec!(0u8, 1, 2, 3, 4, 5, 6, 7));
|
||||
let mut buf = [];
|
||||
assert_eq!(reader.read(&mut buf), Ok(0));
|
||||
assert_eq!(reader.position(), 0);
|
||||
let mut buf = [0];
|
||||
assert_eq!(reader.read(&mut buf), Ok(1));
|
||||
assert_eq!(reader.position(), 1);
|
||||
let b: &[_] = &[0];
|
||||
assert_eq!(buf, b);
|
||||
let mut buf = [0; 4];
|
||||
assert_eq!(reader.read(&mut buf), Ok(4));
|
||||
assert_eq!(reader.position(), 5);
|
||||
let b: &[_] = &[1, 2, 3, 4];
|
||||
assert_eq!(buf, b);
|
||||
assert_eq!(reader.read(&mut buf), Ok(3));
|
||||
let b: &[_] = &[5, 6, 7];
|
||||
assert_eq!(&buf[..3], b);
|
||||
assert_eq!(reader.read(&mut buf), Ok(0));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn read_to_end() {
|
||||
let mut reader = Cursor::new(vec!(0u8, 1, 2, 3, 4, 5, 6, 7));
|
||||
let mut v = Vec::new();
|
||||
reader.read_to_end(&mut v).ok().unwrap();
|
||||
assert_eq!(v, [0, 1, 2, 3, 4, 5, 6, 7]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_slice_reader() {
|
||||
let in_buf = vec![0u8, 1, 2, 3, 4, 5, 6, 7];
|
||||
let mut reader = &mut in_buf.as_slice();
|
||||
let mut buf = [];
|
||||
assert_eq!(reader.read(&mut buf), Ok(0));
|
||||
let mut buf = [0];
|
||||
assert_eq!(reader.read(&mut buf), Ok(1));
|
||||
assert_eq!(reader.len(), 7);
|
||||
let b: &[_] = &[0];
|
||||
assert_eq!(buf.as_slice(), b);
|
||||
let mut buf = [0; 4];
|
||||
assert_eq!(reader.read(&mut buf), Ok(4));
|
||||
assert_eq!(reader.len(), 3);
|
||||
let b: &[_] = &[1, 2, 3, 4];
|
||||
assert_eq!(buf.as_slice(), b);
|
||||
assert_eq!(reader.read(&mut buf), Ok(3));
|
||||
let b: &[_] = &[5, 6, 7];
|
||||
assert_eq!(&buf[..3], b);
|
||||
assert_eq!(reader.read(&mut buf), Ok(0));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_buf_reader() {
|
||||
let in_buf = vec![0u8, 1, 2, 3, 4, 5, 6, 7];
|
||||
let mut reader = Cursor::new(in_buf.as_slice());
|
||||
let mut buf = [];
|
||||
assert_eq!(reader.read(&mut buf), Ok(0));
|
||||
assert_eq!(reader.position(), 0);
|
||||
let mut buf = [0];
|
||||
assert_eq!(reader.read(&mut buf), Ok(1));
|
||||
assert_eq!(reader.position(), 1);
|
||||
let b: &[_] = &[0];
|
||||
assert_eq!(buf, b);
|
||||
let mut buf = [0; 4];
|
||||
assert_eq!(reader.read(&mut buf), Ok(4));
|
||||
assert_eq!(reader.position(), 5);
|
||||
let b: &[_] = &[1, 2, 3, 4];
|
||||
assert_eq!(buf, b);
|
||||
assert_eq!(reader.read(&mut buf), Ok(3));
|
||||
let b: &[_] = &[5, 6, 7];
|
||||
assert_eq!(&buf[..3], b);
|
||||
assert_eq!(reader.read(&mut buf), Ok(0));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_read_char() {
|
||||
let b = b"Vi\xE1\xBB\x87t";
|
||||
let mut c = Cursor::new(b).chars();
|
||||
assert_eq!(c.next(), Some(Ok('V')));
|
||||
assert_eq!(c.next(), Some(Ok('i')));
|
||||
assert_eq!(c.next(), Some(Ok('ệ')));
|
||||
assert_eq!(c.next(), Some(Ok('t')));
|
||||
assert_eq!(c.next(), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_read_bad_char() {
|
||||
let b = b"\x80";
|
||||
let mut c = Cursor::new(b).chars();
|
||||
assert!(c.next().unwrap().is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn seek_past_end() {
|
||||
let buf = [0xff];
|
||||
let mut r = Cursor::new(&buf[]);
|
||||
assert_eq!(r.seek(SeekFrom::Start(10)), Ok(10));
|
||||
assert_eq!(r.read(&mut [0]), Ok(0));
|
||||
|
||||
let mut r = Cursor::new(vec!(10u8));
|
||||
assert_eq!(r.seek(SeekFrom::Start(10)), Ok(10));
|
||||
assert_eq!(r.read(&mut [0]), Ok(0));
|
||||
|
||||
let mut buf = [0];
|
||||
let mut r = Cursor::new(&mut buf[]);
|
||||
assert_eq!(r.seek(SeekFrom::Start(10)), Ok(10));
|
||||
assert_eq!(r.write(&[3]), Ok(0));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn seek_before_0() {
|
||||
let buf = [0xff_u8];
|
||||
let mut r = Cursor::new(&buf[]);
|
||||
assert!(r.seek(SeekFrom::End(-2)).is_err());
|
||||
|
||||
let mut r = Cursor::new(vec!(10u8));
|
||||
assert!(r.seek(SeekFrom::End(-2)).is_err());
|
||||
|
||||
let mut buf = [0];
|
||||
let mut r = Cursor::new(&mut buf[]);
|
||||
assert!(r.seek(SeekFrom::End(-2)).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_seekable_mem_writer() {
|
||||
let mut writer = Cursor::new(Vec::<u8>::new());
|
||||
assert_eq!(writer.position(), 0);
|
||||
assert_eq!(writer.write(&[0]), Ok(1));
|
||||
assert_eq!(writer.position(), 1);
|
||||
assert_eq!(writer.write(&[1, 2, 3]), Ok(3));
|
||||
assert_eq!(writer.write(&[4, 5, 6, 7]), Ok(4));
|
||||
assert_eq!(writer.position(), 8);
|
||||
let b: &[_] = &[0, 1, 2, 3, 4, 5, 6, 7];
|
||||
assert_eq!(&writer.get_ref()[], b);
|
||||
|
||||
assert_eq!(writer.seek(SeekFrom::Start(0)), Ok(0));
|
||||
assert_eq!(writer.position(), 0);
|
||||
assert_eq!(writer.write(&[3, 4]), Ok(2));
|
||||
let b: &[_] = &[3, 4, 2, 3, 4, 5, 6, 7];
|
||||
assert_eq!(&writer.get_ref()[], b);
|
||||
|
||||
assert_eq!(writer.seek(SeekFrom::Current(1)), Ok(3));
|
||||
assert_eq!(writer.write(&[0, 1]), Ok(2));
|
||||
let b: &[_] = &[3, 4, 2, 0, 1, 5, 6, 7];
|
||||
assert_eq!(&writer.get_ref()[], b);
|
||||
|
||||
assert_eq!(writer.seek(SeekFrom::End(-1)), Ok(7));
|
||||
assert_eq!(writer.write(&[1, 2]), Ok(2));
|
||||
let b: &[_] = &[3, 4, 2, 0, 1, 5, 6, 1, 2];
|
||||
assert_eq!(&writer.get_ref()[], b);
|
||||
|
||||
assert_eq!(writer.seek(SeekFrom::End(1)), Ok(10));
|
||||
assert_eq!(writer.write(&[1]), Ok(1));
|
||||
let b: &[_] = &[3, 4, 2, 0, 1, 5, 6, 1, 2, 0, 1];
|
||||
assert_eq!(&writer.get_ref()[], b);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn vec_seek_past_end() {
|
||||
let mut r = Cursor::new(Vec::new());
|
||||
assert_eq!(r.seek(SeekFrom::Start(10)), Ok(10));
|
||||
assert_eq!(r.write(&[3]), Ok(1));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn vec_seek_before_0() {
|
||||
let mut r = Cursor::new(Vec::new());
|
||||
assert!(r.seek(SeekFrom::End(-2)).is_err());
|
||||
}
|
||||
}
|
183
src/libstd/io/error.rs
Normal file
183
src/libstd/io/error.rs
Normal file
|
@ -0,0 +1,183 @@
|
|||
// Copyright 2015 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.
|
||||
|
||||
use boxed::Box;
|
||||
use clone::Clone;
|
||||
use error::Error as StdError;
|
||||
use fmt;
|
||||
use option::Option::{self, Some, None};
|
||||
use result;
|
||||
use string::String;
|
||||
use sys;
|
||||
|
||||
/// A type for results generated by I/O related functions where the `Err` type
|
||||
/// is hard-wired to `io::Error`.
|
||||
///
|
||||
/// This typedef is generally used to avoid writing out `io::Error` directly and
|
||||
/// is otherwise a direct mapping to `std::result::Result`.
|
||||
pub type Result<T> = result::Result<T, Error>;
|
||||
|
||||
/// The error type for I/O operations of the `Read`, `Write`, `Seek`, and
|
||||
/// associated traits.
|
||||
///
|
||||
/// Errors mostly originate from the underlying OS, but custom instances of
|
||||
/// `Error` can be created with crafted error messages and a particular value of
|
||||
/// `ErrorKind`.
|
||||
#[derive(PartialEq, Eq, Clone, Debug)]
|
||||
pub struct Error {
|
||||
repr: Repr,
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Eq, Clone, Debug)]
|
||||
enum Repr {
|
||||
Os(i32),
|
||||
Custom(Box<Custom>),
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Eq, Clone, Debug)]
|
||||
struct Custom {
|
||||
kind: ErrorKind,
|
||||
desc: &'static str,
|
||||
detail: Option<String>
|
||||
}
|
||||
|
||||
/// A list specifying general categories of I/O error.
|
||||
#[derive(Copy, PartialEq, Eq, Clone, Debug)]
|
||||
pub enum ErrorKind {
|
||||
/// The file was not found.
|
||||
FileNotFound,
|
||||
/// The file permissions disallowed access to this file.
|
||||
PermissionDenied,
|
||||
/// The connection was refused by the remote server.
|
||||
ConnectionRefused,
|
||||
/// The connection was reset by the remote server.
|
||||
ConnectionReset,
|
||||
/// The connection was aborted (terminated) by the remote server.
|
||||
ConnectionAborted,
|
||||
/// The network operation failed because it was not connected yet.
|
||||
NotConnected,
|
||||
/// The operation failed because a pipe was closed.
|
||||
BrokenPipe,
|
||||
/// A file already existed with that name.
|
||||
PathAlreadyExists,
|
||||
/// No file exists at that location.
|
||||
PathDoesntExist,
|
||||
/// The path did not specify the type of file that this operation required.
|
||||
/// For example, attempting to copy a directory with the `fs::copy()`
|
||||
/// operation will fail with this error.
|
||||
MismatchedFileTypeForOperation,
|
||||
/// The operation temporarily failed (for example, because a signal was
|
||||
/// received), and retrying may succeed.
|
||||
ResourceUnavailable,
|
||||
/// A parameter was incorrect in a way that caused an I/O error not part of
|
||||
/// this list.
|
||||
InvalidInput,
|
||||
/// The I/O operation's timeout expired, causing it to be canceled.
|
||||
TimedOut,
|
||||
/// An error returned when an operation could not be completed because a
|
||||
/// call to `write` returned `Ok(0)`.
|
||||
///
|
||||
/// This typically means that an operation could only succeed if it wrote a
|
||||
/// particular number of bytes but only a smaller number of bytes could be
|
||||
/// written.
|
||||
WriteZero,
|
||||
/// This operation was interrupted
|
||||
Interrupted,
|
||||
/// Any I/O error not part of this list.
|
||||
Other,
|
||||
}
|
||||
|
||||
impl Error {
|
||||
/// Creates a new custom error from a specified kind/description/detail.
|
||||
pub fn new(kind: ErrorKind,
|
||||
description: &'static str,
|
||||
detail: Option<String>) -> Error {
|
||||
Error {
|
||||
repr: Repr::Custom(Box::new(Custom {
|
||||
kind: kind,
|
||||
desc: description,
|
||||
detail: detail,
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns an error representing the last OS error which occurred.
|
||||
///
|
||||
/// This function reads the value of `errno` for the target platform (e.g.
|
||||
/// `GetLastError` on Windows) and will return a corresponding instance of
|
||||
/// `Error` for the error code.
|
||||
pub fn last_os_error() -> Error {
|
||||
Error::from_os_error(sys::os::errno() as i32)
|
||||
}
|
||||
|
||||
/// Creates a new instance of an `Error` from a particular OS error code.
|
||||
pub fn from_os_error(code: i32) -> Error {
|
||||
Error { repr: Repr::Os(code) }
|
||||
}
|
||||
|
||||
/// Return the corresponding `ErrorKind` for this error.
|
||||
pub fn kind(&self) -> ErrorKind {
|
||||
match self.repr {
|
||||
Repr::Os(code) => sys::decode_error_kind(code),
|
||||
Repr::Custom(ref c) => c.kind,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns a short description for this error message
|
||||
pub fn description(&self) -> &str {
|
||||
match self.repr {
|
||||
Repr::Os(..) => "os error",
|
||||
Repr::Custom(ref c) => c.desc,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns a detailed error message for this error (if one is available)
|
||||
pub fn detail(&self) -> Option<String> {
|
||||
match self.repr {
|
||||
Repr::Os(code) => Some(sys::os::error_string(code)),
|
||||
Repr::Custom(ref s) => s.detail.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Error {
|
||||
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
||||
match self.repr {
|
||||
Repr::Os(code) => {
|
||||
let detail = sys::os::error_string(code);
|
||||
write!(fmt, "{} (os error {})", detail, code)
|
||||
}
|
||||
Repr::Custom(ref c) => {
|
||||
match **c {
|
||||
Custom {
|
||||
kind: ErrorKind::Other,
|
||||
desc: "unknown error",
|
||||
detail: Some(ref detail)
|
||||
} => {
|
||||
write!(fmt, "{}", detail)
|
||||
}
|
||||
Custom { detail: None, desc, .. } =>
|
||||
write!(fmt, "{}", desc),
|
||||
Custom { detail: Some(ref detail), desc, .. } =>
|
||||
write!(fmt, "{} ({})", desc, detail)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl StdError for Error {
|
||||
fn description(&self) -> &str {
|
||||
match self.repr {
|
||||
Repr::Os(..) => "os error",
|
||||
Repr::Custom(ref c) => c.desc,
|
||||
}
|
||||
}
|
||||
}
|
88
src/libstd/io/impls.rs
Normal file
88
src/libstd/io/impls.rs
Normal file
|
@ -0,0 +1,88 @@
|
|||
// Copyright 2015 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.
|
||||
|
||||
use core::prelude::*;
|
||||
|
||||
use boxed::Box;
|
||||
use cmp;
|
||||
use io::{self, SeekFrom, Read, Write, Seek, BufRead};
|
||||
use mem;
|
||||
use slice;
|
||||
use vec::Vec;
|
||||
|
||||
// =============================================================================
|
||||
// Forwarding implementations
|
||||
|
||||
impl<'a, R: Read + ?Sized> Read for &'a mut R {
|
||||
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> { (**self).read(buf) }
|
||||
}
|
||||
impl<'a, W: Write + ?Sized> Write for &'a mut W {
|
||||
fn write(&mut self, buf: &[u8]) -> io::Result<usize> { (**self).write(buf) }
|
||||
fn flush(&mut self) -> io::Result<()> { (**self).flush() }
|
||||
}
|
||||
impl<'a, S: Seek + ?Sized> Seek for &'a mut S {
|
||||
fn seek(&mut self, pos: SeekFrom) -> io::Result<u64> { (**self).seek(pos) }
|
||||
}
|
||||
impl<'a, B: BufRead + ?Sized> BufRead for &'a mut B {
|
||||
fn fill_buf(&mut self) -> io::Result<&[u8]> { (**self).fill_buf() }
|
||||
fn consume(&mut self, amt: usize) { (**self).consume(amt) }
|
||||
}
|
||||
|
||||
impl<R: Read + ?Sized> Read for Box<R> {
|
||||
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> { (**self).read(buf) }
|
||||
}
|
||||
impl<W: Write + ?Sized> Write for Box<W> {
|
||||
fn write(&mut self, buf: &[u8]) -> io::Result<usize> { (**self).write(buf) }
|
||||
fn flush(&mut self) -> io::Result<()> { (**self).flush() }
|
||||
}
|
||||
impl<S: Seek + ?Sized> Seek for Box<S> {
|
||||
fn seek(&mut self, pos: SeekFrom) -> io::Result<u64> { (**self).seek(pos) }
|
||||
}
|
||||
impl<B: BufRead + ?Sized> BufRead for Box<B> {
|
||||
fn fill_buf(&mut self) -> io::Result<&[u8]> { (**self).fill_buf() }
|
||||
fn consume(&mut self, amt: usize) { (**self).consume(amt) }
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// In-memory buffer implementations
|
||||
|
||||
impl<'a> Read for &'a [u8] {
|
||||
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
|
||||
let amt = cmp::min(buf.len(), self.len());
|
||||
let (a, b) = self.split_at(amt);
|
||||
slice::bytes::copy_memory(buf, a);
|
||||
*self = b;
|
||||
Ok(amt)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> BufRead for &'a [u8] {
|
||||
fn fill_buf(&mut self) -> io::Result<&[u8]> { Ok(*self) }
|
||||
fn consume(&mut self, amt: usize) { *self = &self[amt..]; }
|
||||
}
|
||||
|
||||
impl<'a> Write for &'a mut [u8] {
|
||||
fn write(&mut self, data: &[u8]) -> io::Result<usize> {
|
||||
let amt = cmp::min(data.len(), self.len());
|
||||
let (a, b) = mem::replace(self, &mut []).split_at_mut(amt);
|
||||
slice::bytes::copy_memory(a, &data[..amt]);
|
||||
*self = b;
|
||||
Ok(amt)
|
||||
}
|
||||
fn flush(&mut self) -> io::Result<()> { Ok(()) }
|
||||
}
|
||||
|
||||
impl Write for Vec<u8> {
|
||||
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
|
||||
self.push_all(buf);
|
||||
Ok(buf.len())
|
||||
}
|
||||
fn flush(&mut self) -> io::Result<()> { Ok(()) }
|
||||
}
|
948
src/libstd/io/mod.rs
Normal file
948
src/libstd/io/mod.rs
Normal file
|
@ -0,0 +1,948 @@
|
|||
// Copyright 2015 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.
|
||||
|
||||
//! Traits, helpers, and type definitions for core I/O functionality.
|
||||
//!
|
||||
//! > **NOTE**: This module is very much a work in progress and is under active
|
||||
//! > development. At this time it is still recommended to use the `old_io`
|
||||
//! > module while the details of this module shake out.
|
||||
|
||||
#![unstable(feature = "io",
|
||||
reason = "this new I/O module is still under active deveopment and \
|
||||
APIs are subject to tweaks fairly regularly")]
|
||||
|
||||
use cmp;
|
||||
use unicode::str as core_str;
|
||||
use error::Error as StdError;
|
||||
use fmt;
|
||||
use iter::Iterator;
|
||||
use marker::Sized;
|
||||
use mem;
|
||||
use ops::{Drop, FnOnce};
|
||||
use option::Option::{self, Some, None};
|
||||
use ptr::PtrExt;
|
||||
use result::Result::{Ok, Err};
|
||||
use result;
|
||||
use slice::{self, SliceExt};
|
||||
use string::String;
|
||||
use str::{self, StrExt};
|
||||
use vec::Vec;
|
||||
|
||||
pub use self::buffered::{BufReader, BufWriter, BufStream, LineWriter};
|
||||
pub use self::buffered::IntoInnerError;
|
||||
pub use self::cursor::Cursor;
|
||||
pub use self::error::{Result, Error, ErrorKind};
|
||||
pub use self::util::{copy, sink, Sink, empty, Empty, repeat, Repeat};
|
||||
|
||||
pub mod prelude;
|
||||
mod buffered;
|
||||
mod cursor;
|
||||
mod error;
|
||||
mod impls;
|
||||
mod util;
|
||||
|
||||
const DEFAULT_BUF_SIZE: usize = 64 * 1024;
|
||||
|
||||
// Acquires a slice of the vector `v` from its length to its capacity
|
||||
// (uninitialized data), reads into it, and then updates the length.
|
||||
//
|
||||
// This function is leveraged to efficiently read some bytes into a destination
|
||||
// vector without extra copying and taking advantage of the space that's already
|
||||
// in `v`.
|
||||
//
|
||||
// The buffer we're passing down, however, is pointing at uninitialized data
|
||||
// (the end of a `Vec`), and many operations will be *much* faster if we don't
|
||||
// have to zero it out. In order to prevent LLVM from generating an `undef`
|
||||
// value when reads happen from this uninitialized memory, we force LLVM to
|
||||
// think it's initialized by sending it through a black box. This should prevent
|
||||
// actual undefined behavior after optimizations.
|
||||
fn with_end_to_cap<F>(v: &mut Vec<u8>, f: F) -> Result<usize>
|
||||
where F: FnOnce(&mut [u8]) -> Result<usize>
|
||||
{
|
||||
unsafe {
|
||||
let n = try!(f({
|
||||
let base = v.as_mut_ptr().offset(v.len() as isize);
|
||||
black_box(slice::from_raw_mut_buf(mem::copy_lifetime(v, &base),
|
||||
v.capacity() - v.len()))
|
||||
}));
|
||||
|
||||
// If the closure (typically a `read` implementation) reported that it
|
||||
// read a larger number of bytes than the vector actually has, we need
|
||||
// to be sure to clamp the vector to at most its capacity.
|
||||
let new_len = cmp::min(v.capacity(), v.len() + n);
|
||||
v.set_len(new_len);
|
||||
return Ok(n);
|
||||
}
|
||||
|
||||
// Semi-hack used to prevent LLVM from retaining any assumptions about
|
||||
// `dummy` over this function call
|
||||
unsafe fn black_box<T>(mut dummy: T) -> T {
|
||||
asm!("" :: "r"(&mut dummy) : "memory");
|
||||
dummy
|
||||
}
|
||||
}
|
||||
|
||||
// A few methods below (read_to_string, read_line) will append data into a
|
||||
// `String` buffer, but we need to be pretty careful when doing this. The
|
||||
// implementation will just call `.as_mut_vec()` and then delegate to a
|
||||
// byte-oriented reading method, but we must ensure that when returning we never
|
||||
// leave `buf` in a state such that it contains invalid UTF-8 in its bounds.
|
||||
//
|
||||
// To this end, we use an RAII guard (to protect against panics) which updates
|
||||
// the length of the string when it is dropped. This guard initially truncates
|
||||
// the string to the prior length and only afer we've validated that the
|
||||
// new contents are valid UTF-8 do we allow it to set a longer length.
|
||||
//
|
||||
// The unsafety in this function is twofold:
|
||||
//
|
||||
// 1. We're looking at the raw bytes of `buf`, so we take on the burden of UTF-8
|
||||
// checks.
|
||||
// 2. We're passing a raw buffer to the function `f`, and it is expected that
|
||||
// the function only *appends* bytes to the buffer. We'll get undefined
|
||||
// behavior if existing bytes are overwritten to have non-UTF-8 data.
|
||||
fn append_to_string<F>(buf: &mut String, f: F) -> Result<()>
|
||||
where F: FnOnce(&mut Vec<u8>) -> Result<()>
|
||||
{
|
||||
struct Guard<'a> { s: &'a mut Vec<u8>, len: usize }
|
||||
#[unsafe_destructor]
|
||||
impl<'a> Drop for Guard<'a> {
|
||||
fn drop(&mut self) {
|
||||
unsafe { self.s.set_len(self.len); }
|
||||
}
|
||||
}
|
||||
|
||||
unsafe {
|
||||
let mut g = Guard { len: buf.len(), s: buf.as_mut_vec() };
|
||||
let ret = f(g.s);
|
||||
if str::from_utf8(&g.s[g.len..]).is_err() {
|
||||
ret.and_then(|()| {
|
||||
Err(Error::new(ErrorKind::InvalidInput,
|
||||
"stream did not contain valid UTF-8", None))
|
||||
})
|
||||
} else {
|
||||
g.len = g.s.len();
|
||||
ret
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn read_to_end<R: Read + ?Sized>(r: &mut R, buf: &mut Vec<u8>) -> Result<()> {
|
||||
loop {
|
||||
if buf.capacity() == buf.len() {
|
||||
buf.reserve(DEFAULT_BUF_SIZE);
|
||||
}
|
||||
match with_end_to_cap(buf, |b| r.read(b)) {
|
||||
Ok(0) => return Ok(()),
|
||||
Ok(_) => {}
|
||||
Err(ref e) if e.kind() == ErrorKind::Interrupted => {}
|
||||
Err(e) => return Err(e),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A trait for objects which are byte-oriented sources.
|
||||
///
|
||||
/// Readers are defined by one method, `read`. Each call to `read` will attempt
|
||||
/// to pull bytes from this source into a provided buffer.
|
||||
///
|
||||
/// Readers are intended to be composable with one another. Many objects
|
||||
/// throughout the I/O and related libraries take and provide types which
|
||||
/// implement the `Read` trait.
|
||||
pub trait Read {
|
||||
/// Pull some bytes from this source into the specified buffer, returning
|
||||
/// how many bytes were read.
|
||||
///
|
||||
/// This function does not provide any guarantees about whether it blocks
|
||||
/// waiting for data, but if an object needs to block for a read but cannot
|
||||
/// it will typically signal this via an `Err` return value.
|
||||
///
|
||||
/// If the return value of this method is `Ok(n)`, then it must be
|
||||
/// guaranteed that `0 <= n <= buf.len()`. A nonzero `n` value indicates
|
||||
/// that the buffer `buf` has ben filled in with `n` bytes of data from this
|
||||
/// source. If `n` is `0`, then it can indicate one of two scenarios:
|
||||
///
|
||||
/// 1. This reader has reached its "end of file" and will likely no longer
|
||||
/// be able to produce bytes. Note that this does not mean that the
|
||||
/// reader will *always* no longer be able to produce bytes.
|
||||
/// 2. The buffer specified was 0 bytes in length.
|
||||
///
|
||||
/// No guarantees are provided about the contents of `buf` when this
|
||||
/// function is called, implementations cannot rely on any property of the
|
||||
/// contents of `buf` being true. It is recommended that implementations
|
||||
/// only write data to `buf` instead of reading its contents.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// If this function encounters any form of I/O or other error, an error
|
||||
/// variant will be returned. If an error is returned then it must be
|
||||
/// guaranteed that no bytes were read.
|
||||
fn read(&mut self, buf: &mut [u8]) -> Result<usize>;
|
||||
|
||||
/// Read all bytes until EOF in this source, placing them into `buf`.
|
||||
///
|
||||
/// All bytes read from this source will be appended to the specified buffer
|
||||
/// `buf`. This function will return a call to `read` either:
|
||||
///
|
||||
/// 1. Returns `Ok(0)`.
|
||||
/// 2. Returns an error which is not of the kind `ErrorKind::Interrupted`.
|
||||
///
|
||||
/// Until one of these conditions is met the function will continuously
|
||||
/// invoke `read` to append more data to `buf`.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// If this function encounters an error of the kind
|
||||
/// `ErrorKind::Interrupted` then the error is ignored and the operation
|
||||
/// will continue.
|
||||
///
|
||||
/// If any other read error is encountered then this function immediately
|
||||
/// returns. Any bytes which have already been read will be appended to
|
||||
/// `buf`.
|
||||
fn read_to_end(&mut self, buf: &mut Vec<u8>) -> Result<()> {
|
||||
read_to_end(self, buf)
|
||||
}
|
||||
|
||||
/// Read all bytes until EOF in this source, placing them into `buf`.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// If the data in this stream is *not* valid UTF-8 then an error is
|
||||
/// returned and `buf` is unchanged.
|
||||
///
|
||||
/// See `read_to_end` for other error semantics.
|
||||
fn read_to_string(&mut self, buf: &mut String) -> Result<()> {
|
||||
// Note that we do *not* call `.read_to_end()` here. We are passing
|
||||
// `&mut Vec<u8>` (the raw contents of `buf`) into the `read_to_end`
|
||||
// method to fill it up. An arbitrary implementation could overwrite the
|
||||
// entire contents of the vector, not just append to it (which is what
|
||||
// we are expecting).
|
||||
//
|
||||
// To prevent extraneously checking the UTF-8-ness of the entire buffer
|
||||
// we pass it to our hardcoded `read_to_end` implementation which we
|
||||
// know is guaranteed to only read data into the end of the buffer.
|
||||
append_to_string(buf, |b| read_to_end(self, b))
|
||||
}
|
||||
}
|
||||
|
||||
/// Extension methods for all instances of `Read`, typically imported through
|
||||
/// `std::io::prelude::*`.
|
||||
pub trait ReadExt: Read + Sized {
|
||||
/// Create a "by reference" adaptor for this instance of `Read`.
|
||||
///
|
||||
/// The returned adaptor also implements `Read` and will simply borrow this
|
||||
/// current reader.
|
||||
fn by_ref(&mut self) -> &mut Self { self }
|
||||
|
||||
/// Transform this `Read` instance to an `Iterator` over its bytes.
|
||||
///
|
||||
/// The returned type implements `Iterator` where the `Item` is `Result<u8,
|
||||
/// R::Err>`. The yielded item is `Ok` if a byte was successfully read and
|
||||
/// `Err` otherwise for I/O errors. EOF is mapped to returning `None` from
|
||||
/// this iterator.
|
||||
fn bytes(self) -> Bytes<Self> {
|
||||
Bytes { inner: self }
|
||||
}
|
||||
|
||||
/// Transform this `Read` instance to an `Iterator` over `char`s.
|
||||
///
|
||||
/// This adaptor will attempt to interpret this reader as an UTF-8 encoded
|
||||
/// sequence of characters. The returned iterator will return `None` once
|
||||
/// EOF is reached for this reader. Otherwise each element yielded will be a
|
||||
/// `Result<char, E>` where `E` may contain information about what I/O error
|
||||
/// occurred or where decoding failed.
|
||||
///
|
||||
/// Currently this adaptor will discard intermediate data read, and should
|
||||
/// be avoided if this is not desired.
|
||||
fn chars(self) -> Chars<Self> {
|
||||
Chars { inner: self }
|
||||
}
|
||||
|
||||
/// Create an adaptor which will chain this stream with another.
|
||||
///
|
||||
/// The returned `Read` instance will first read all bytes from this object
|
||||
/// until EOF is encountered. Afterwards the output is equivalent to the
|
||||
/// output of `next`.
|
||||
fn chain<R: Read>(self, next: R) -> Chain<Self, R> {
|
||||
Chain { first: self, second: next, done_first: false }
|
||||
}
|
||||
|
||||
/// Create an adaptor which will read at most `limit` bytes from it.
|
||||
///
|
||||
/// This function returns a new instance of `Read` which will read at most
|
||||
/// `limit` bytes, after which it will always return EOF (`Ok(0)`). Any
|
||||
/// read errors will not count towards the number of bytes read and future
|
||||
/// calls to `read` may succeed.
|
||||
fn take(self, limit: u64) -> Take<Self> {
|
||||
Take { inner: self, limit: limit }
|
||||
}
|
||||
|
||||
/// Creates a reader adaptor which will write all read data into the given
|
||||
/// output stream.
|
||||
///
|
||||
/// Whenever the returned `Read` instance is read it will write the read
|
||||
/// data to `out`. The current semantics of this implementation imply that
|
||||
/// a `write` error will not report how much data was initially read.
|
||||
fn tee<W: Write>(self, out: W) -> Tee<Self, W> {
|
||||
Tee { reader: self, writer: out }
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Read> ReadExt for T {}
|
||||
|
||||
/// A trait for objects which are byte-oriented sinks.
|
||||
///
|
||||
/// The `write` method will attempt to write some data into the object,
|
||||
/// returning how many bytes were successfully written.
|
||||
///
|
||||
/// The `flush` method is useful for adaptors and explicit buffers themselves
|
||||
/// for ensuring that all buffered data has been pushed out to the "true sink".
|
||||
///
|
||||
/// Writers are intended to be composable with one another. Many objects
|
||||
/// throughout the I/O and related libraries take and provide types which
|
||||
/// implement the `Write` trait.
|
||||
pub trait Write {
|
||||
/// Write a buffer into this object, returning how many bytes were written.
|
||||
///
|
||||
/// This function will attempt to write the entire contents of `buf`, but
|
||||
/// the entire write may not succeed, or the write may also generate an
|
||||
/// error. A call to `write` represents *at most one* attempt to write to
|
||||
/// any wrapped object.
|
||||
///
|
||||
/// Calls to `write` are not guaranteed to block waiting for data to be
|
||||
/// written, and a write which would otherwise block can indicated through
|
||||
/// an `Err` variant.
|
||||
///
|
||||
/// If the return value is `Ok(n)` then it must be guaranteed that
|
||||
/// `0 <= n <= buf.len()`. A return value of `0` typically means that the
|
||||
/// underlying object is no longer able to accept bytes and will likely not
|
||||
/// be able to in the future as well, or that the buffer provided is empty.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// Each call to `write` may generate an I/O error indicating that the
|
||||
/// operation could not be completed. If an error is returned then no bytes
|
||||
/// in the buffer were written to this writer.
|
||||
///
|
||||
/// It is **not** considered an error if the entire buffer could not be
|
||||
/// written to this writer.
|
||||
fn write(&mut self, buf: &[u8]) -> Result<usize>;
|
||||
|
||||
/// Flush this output stream, ensuring that all intermediately buffered
|
||||
/// contents reach their destination.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// It is considered an error if not all bytes could be written due to
|
||||
/// I/O errors or EOF being reached.
|
||||
fn flush(&mut self) -> Result<()>;
|
||||
|
||||
/// Attempts to write an entire buffer into this write.
|
||||
///
|
||||
/// This method will continuously call `write` while there is more data to
|
||||
/// write. This method will not return until the entire buffer has been
|
||||
/// successfully written or an error occurs. The first error generated from
|
||||
/// this method will be returned.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// This function will return the first error that `write` returns.
|
||||
fn write_all(&mut self, mut buf: &[u8]) -> Result<()> {
|
||||
while buf.len() > 0 {
|
||||
match self.write(buf) {
|
||||
Ok(0) => return Err(Error::new(ErrorKind::WriteZero,
|
||||
"failed to write whole buffer",
|
||||
None)),
|
||||
Ok(n) => buf = &buf[n..],
|
||||
Err(ref e) if e.kind() == ErrorKind::Interrupted => {}
|
||||
Err(e) => return Err(e),
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Writes a formatted string into this writer, returning any error
|
||||
/// encountered.
|
||||
///
|
||||
/// This method is primarily used to interface with the `format_args!`
|
||||
/// macro, but it is rare that this should explicitly be called. The
|
||||
/// `write!` macro should be favored to invoke this method instead.
|
||||
///
|
||||
/// This function internally uses the `write_all` method on this trait and
|
||||
/// hence will continuously write data so long as no errors are received.
|
||||
/// This also means that partial writes are not indicated in this signature.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// This function will return any I/O error reported while formatting.
|
||||
fn write_fmt(&mut self, fmt: fmt::Arguments) -> Result<()> {
|
||||
// Create a shim which translates a Writer to a fmt::Writer and saves
|
||||
// off I/O errors. instead of discarding them
|
||||
struct Adaptor<'a, T: ?Sized + 'a> {
|
||||
inner: &'a mut T,
|
||||
error: Result<()>,
|
||||
}
|
||||
|
||||
impl<'a, T: Write + ?Sized> fmt::Writer for Adaptor<'a, T> {
|
||||
fn write_str(&mut self, s: &str) -> fmt::Result {
|
||||
match self.inner.write_all(s.as_bytes()) {
|
||||
Ok(()) => Ok(()),
|
||||
Err(e) => {
|
||||
self.error = Err(e);
|
||||
Err(fmt::Error)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let mut output = Adaptor { inner: self, error: Ok(()) };
|
||||
match fmt::write(&mut output, fmt) {
|
||||
Ok(()) => Ok(()),
|
||||
Err(..) => output.error
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Extension methods for all instances of `Write`, typically imported through
|
||||
/// `std::io::prelude::*`.
|
||||
pub trait WriteExt: Write + Sized {
|
||||
/// Create a "by reference" adaptor for this instance of `Write`.
|
||||
///
|
||||
/// The returned adaptor also implements `Write` and will simply borrow this
|
||||
/// current writer.
|
||||
fn by_ref(&mut self) -> &mut Self { self }
|
||||
|
||||
/// Creates a new writer which will write all data to both this writer and
|
||||
/// another writer.
|
||||
///
|
||||
/// All data written to the returned writer will both be written to `self`
|
||||
/// as well as `other`. Note that the error semantics of the current
|
||||
/// implementation do not precisely track where errors happen. For example
|
||||
/// an error on the second call to `write` will not report that the first
|
||||
/// call to `write` succeeded.
|
||||
fn broadcast<W: Write>(self, other: W) -> Broadcast<Self, W> {
|
||||
Broadcast { first: self, second: other }
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Write> WriteExt for T {}
|
||||
|
||||
/// An object implementing `Seek` internally has some form of cursor which can
|
||||
/// be moved within a stream of bytes.
|
||||
///
|
||||
/// The stream typically has a fixed size, allowing seeking relative to either
|
||||
/// end or the current offset.
|
||||
pub trait Seek {
|
||||
/// Seek to an offset, in bytes, in a stream
|
||||
///
|
||||
/// A seek beyond the end of a stream is allowed, but seeking before offset
|
||||
/// 0 is an error.
|
||||
///
|
||||
/// Seeking past the end of the stream does not modify the underlying
|
||||
/// stream, but the next write may cause the previous data to be filled in
|
||||
/// with a bit pattern.
|
||||
///
|
||||
/// This method returns the new position within the stream if the seek
|
||||
/// operation completed successfully.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// Seeking to a negative offset is considered an error
|
||||
fn seek(&mut self, pos: SeekFrom) -> Result<u64>;
|
||||
}
|
||||
|
||||
/// Enumeration of possible methods to seek within an I/O object.
|
||||
#[derive(Copy, PartialEq, Eq, Clone, Debug)]
|
||||
pub enum SeekFrom {
|
||||
/// Set the offset to the provided number of bytes.
|
||||
Start(u64),
|
||||
|
||||
/// Set the offset to the size of this object plus the specified number of
|
||||
/// bytes.
|
||||
///
|
||||
/// It is possible to seek beyond the end of an object, but is an error to
|
||||
/// seek before byte 0.
|
||||
End(i64),
|
||||
|
||||
/// Set the offset to the current position plus the specified number of
|
||||
/// bytes.
|
||||
///
|
||||
/// It is possible to seek beyond the end of an object, but is an error to
|
||||
/// seek before byte 0.
|
||||
Current(i64),
|
||||
}
|
||||
|
||||
fn read_until<R: BufRead + ?Sized>(r: &mut R, delim: u8, buf: &mut Vec<u8>)
|
||||
-> Result<()> {
|
||||
loop {
|
||||
let (done, used) = {
|
||||
let available = match r.fill_buf() {
|
||||
Ok(n) => n,
|
||||
Err(ref e) if e.kind() == ErrorKind::Interrupted => continue,
|
||||
Err(e) => return Err(e)
|
||||
};
|
||||
match available.position_elem(&delim) {
|
||||
Some(i) => {
|
||||
buf.push_all(&available[..i + 1]);
|
||||
(true, i + 1)
|
||||
}
|
||||
None => {
|
||||
buf.push_all(available);
|
||||
(false, available.len())
|
||||
}
|
||||
}
|
||||
};
|
||||
r.consume(used);
|
||||
if done || used == 0 {
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A Buffer is a type of reader which has some form of internal buffering to
|
||||
/// allow certain kinds of reading operations to be more optimized than others.
|
||||
///
|
||||
/// This type extends the `Read` trait with a few methods that are not
|
||||
/// possible to reasonably implement with purely a read interface.
|
||||
pub trait BufRead: Read {
|
||||
/// Fills the internal buffer of this object, returning the buffer contents.
|
||||
///
|
||||
/// None of the contents will be "read" in the sense that later calling
|
||||
/// `read` may return the same contents.
|
||||
///
|
||||
/// The `consume` function must be called with the number of bytes that are
|
||||
/// consumed from this buffer returned to ensure that the bytes are never
|
||||
/// returned twice.
|
||||
///
|
||||
/// An empty buffer returned indicates that the stream has reached EOF.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// This function will return an I/O error if the underlying reader was
|
||||
/// read, but returned an error.
|
||||
fn fill_buf(&mut self) -> Result<&[u8]>;
|
||||
|
||||
/// Tells this buffer that `amt` bytes have been consumed from the buffer,
|
||||
/// so they should no longer be returned in calls to `read`.
|
||||
fn consume(&mut self, amt: usize);
|
||||
|
||||
/// Read all bytes until the delimiter `byte` is reached.
|
||||
///
|
||||
/// This function will continue to read (and buffer) bytes from the
|
||||
/// underlying stream until the delimiter or EOF is found. Once found, all
|
||||
/// bytes up to, and including, the delimiter (if found) will be appended to
|
||||
/// `buf`.
|
||||
///
|
||||
/// If this buffered reader is currently at EOF, then this function will not
|
||||
/// place any more bytes into `buf` and will return `Ok(())`.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// This function will ignore all instances of `ErrorKind::Interrupted` and
|
||||
/// will otherwise return any errors returned by `fill_buf`.
|
||||
///
|
||||
/// If an I/O error is encountered then all bytes read so far will be
|
||||
/// present in `buf` and its length will have been adjusted appropriately.
|
||||
fn read_until(&mut self, byte: u8, buf: &mut Vec<u8>) -> Result<()> {
|
||||
read_until(self, byte, buf)
|
||||
}
|
||||
|
||||
/// Read all bytes until a newline byte (the 0xA byte) is reached.
|
||||
///
|
||||
/// This function will continue to read (and buffer) bytes from the
|
||||
/// underlying stream until the newline delimiter (the 0xA byte) or EOF is
|
||||
/// found. Once found, all bytes up to, and including, the delimiter (if
|
||||
/// found) will be appended to `buf`.
|
||||
///
|
||||
/// If this reader is currently at EOF then this function will not modify
|
||||
/// `buf` and will return `Ok(())`.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// This function has the same error semantics as `read_until` and will also
|
||||
/// return an error if the read bytes are not valid UTF-8. If an I/O error
|
||||
/// is encountered then `buf` may contain some bytes already read in the
|
||||
/// event that all data read so far was valid UTF-8.
|
||||
fn read_line(&mut self, buf: &mut String) -> Result<()> {
|
||||
// Note that we are not calling the `.read_until` method here, but
|
||||
// rather our hardcoded implementation. For more details as to why, see
|
||||
// the comments in `read_to_end`.
|
||||
append_to_string(buf, |b| read_until(self, b'\n', b))
|
||||
}
|
||||
}
|
||||
|
||||
/// Extension methods for all instances of `BufRead`, typically imported through
|
||||
/// `std::io::prelude::*`.
|
||||
pub trait BufReadExt: BufRead + Sized {
|
||||
/// Returns an iterator over the contents of this reader split on the byte
|
||||
/// `byte`.
|
||||
///
|
||||
/// The iterator returned from this function will return instances of
|
||||
/// `io::Result<Vec<u8>>`. Each vector returned will *not* have the
|
||||
/// delimiter byte at the end.
|
||||
///
|
||||
/// This function will yield errors whenever `read_until` would have also
|
||||
/// yielded an error.
|
||||
fn split(self, byte: u8) -> Split<Self> {
|
||||
Split { buf: self, delim: byte }
|
||||
}
|
||||
|
||||
/// Returns an iterator over the lines of this reader.
|
||||
///
|
||||
/// The iterator returned from this function will yield instances of
|
||||
/// `io::Result<String>`. Each string returned will *not* have a newline
|
||||
/// byte (the 0xA byte) at the end.
|
||||
///
|
||||
/// This function will yield errors whenever `read_string` would have also
|
||||
/// yielded an error.
|
||||
fn lines(self) -> Lines<Self> {
|
||||
Lines { buf: self }
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: BufRead> BufReadExt for T {}
|
||||
|
||||
/// A `Write` adaptor which will write data to multiple locations.
|
||||
///
|
||||
/// For more information, see `WriteExt::broadcast`.
|
||||
pub struct Broadcast<T, U> {
|
||||
first: T,
|
||||
second: U,
|
||||
}
|
||||
|
||||
impl<T: Write, U: Write> Write for Broadcast<T, U> {
|
||||
fn write(&mut self, data: &[u8]) -> Result<usize> {
|
||||
let n = try!(self.first.write(data));
|
||||
// FIXME: what if the write fails? (we wrote something)
|
||||
try!(self.second.write_all(&data[..n]));
|
||||
Ok(n)
|
||||
}
|
||||
|
||||
fn flush(&mut self) -> Result<()> {
|
||||
self.first.flush().and(self.second.flush())
|
||||
}
|
||||
}
|
||||
|
||||
/// Adaptor to chain together two instances of `Read`.
|
||||
///
|
||||
/// For more information, see `ReadExt::chain`.
|
||||
pub struct Chain<T, U> {
|
||||
first: T,
|
||||
second: U,
|
||||
done_first: bool,
|
||||
}
|
||||
|
||||
impl<T: Read, U: Read> Read for Chain<T, U> {
|
||||
fn read(&mut self, buf: &mut [u8]) -> Result<usize> {
|
||||
if !self.done_first {
|
||||
match try!(self.first.read(buf)) {
|
||||
0 => { self.done_first = true; }
|
||||
n => return Ok(n),
|
||||
}
|
||||
}
|
||||
self.second.read(buf)
|
||||
}
|
||||
}
|
||||
|
||||
/// Reader adaptor which limits the bytes read from an underlying reader.
|
||||
///
|
||||
/// For more information, see `ReadExt::take`.
|
||||
pub struct Take<T> {
|
||||
inner: T,
|
||||
limit: u64,
|
||||
}
|
||||
|
||||
impl<T> Take<T> {
|
||||
/// Returns the number of bytes that can be read before this instance will
|
||||
/// return EOF.
|
||||
///
|
||||
/// # Note
|
||||
///
|
||||
/// This instance may reach EOF after reading fewer bytes than indiccated by
|
||||
/// this method if the underlying `Read` instance reaches EOF.
|
||||
pub fn limit(&self) -> u64 { self.limit }
|
||||
}
|
||||
|
||||
impl<T: Read> Read for Take<T> {
|
||||
fn read(&mut self, buf: &mut [u8]) -> Result<usize> {
|
||||
let max = cmp::min(buf.len() as u64, self.limit) as usize;
|
||||
let n = try!(self.inner.read(&mut buf[..max]));
|
||||
self.limit -= n as u64;
|
||||
Ok(n)
|
||||
}
|
||||
}
|
||||
|
||||
/// An adaptor which will emit all read data to a specified writer as well.
|
||||
///
|
||||
/// For more information see `ReadExt::tee`
|
||||
pub struct Tee<R, W> {
|
||||
reader: R,
|
||||
writer: W,
|
||||
}
|
||||
|
||||
impl<R: Read, W: Write> Read for Tee<R, W> {
|
||||
fn read(&mut self, buf: &mut [u8]) -> Result<usize> {
|
||||
let n = try!(self.reader.read(buf));
|
||||
// FIXME: what if the write fails? (we read something)
|
||||
try!(self.writer.write_all(&buf[..n]));
|
||||
Ok(n)
|
||||
}
|
||||
}
|
||||
|
||||
/// A bridge from implementations of `Read` to an `Iterator` of `u8`.
|
||||
///
|
||||
/// See `ReadExt::bytes` for more information.
|
||||
pub struct Bytes<R> {
|
||||
inner: R,
|
||||
}
|
||||
|
||||
impl<R: Read> Iterator for Bytes<R> {
|
||||
type Item = Result<u8>;
|
||||
|
||||
fn next(&mut self) -> Option<Result<u8>> {
|
||||
let mut buf = [0];
|
||||
match self.inner.read(&mut buf) {
|
||||
Ok(0) => None,
|
||||
Ok(..) => Some(Ok(buf[0])),
|
||||
Err(e) => Some(Err(e)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A bridge from implementations of `Read` to an `Iterator` of `char`.
|
||||
///
|
||||
/// See `ReadExt::chars` for more information.
|
||||
pub struct Chars<R> {
|
||||
inner: R,
|
||||
}
|
||||
|
||||
/// An enumeration of possible errors that can be generated from the `Chars`
|
||||
/// adapter.
|
||||
#[derive(PartialEq, Clone, Debug)]
|
||||
pub enum CharsError {
|
||||
/// Variant representing that the underlying stream was read successfully
|
||||
/// but it did not contain valid utf8 data.
|
||||
NotUtf8,
|
||||
|
||||
/// Variant representing that an I/O error occurred.
|
||||
Other(Error),
|
||||
}
|
||||
|
||||
impl<R: Read> Iterator for Chars<R> {
|
||||
type Item = result::Result<char, CharsError>;
|
||||
|
||||
fn next(&mut self) -> Option<result::Result<char, CharsError>> {
|
||||
let mut buf = [0];
|
||||
let first_byte = match self.inner.read(&mut buf) {
|
||||
Ok(0) => return None,
|
||||
Ok(..) => buf[0],
|
||||
Err(e) => return Some(Err(CharsError::Other(e))),
|
||||
};
|
||||
let width = core_str::utf8_char_width(first_byte);
|
||||
if width == 1 { return Some(Ok(first_byte as char)) }
|
||||
if width == 0 { return Some(Err(CharsError::NotUtf8)) }
|
||||
let mut buf = [first_byte, 0, 0, 0];
|
||||
{
|
||||
let mut start = 1;
|
||||
while start < width {
|
||||
match self.inner.read(&mut buf[start..width]) {
|
||||
Ok(0) => return Some(Err(CharsError::NotUtf8)),
|
||||
Ok(n) => start += n,
|
||||
Err(e) => return Some(Err(CharsError::Other(e))),
|
||||
}
|
||||
}
|
||||
}
|
||||
Some(match str::from_utf8(&buf[..width]).ok() {
|
||||
Some(s) => Ok(s.char_at(0)),
|
||||
None => Err(CharsError::NotUtf8),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl StdError for CharsError {
|
||||
fn description(&self) -> &str {
|
||||
match *self {
|
||||
CharsError::NotUtf8 => "invalid utf8 encoding",
|
||||
CharsError::Other(ref e) => e.description(),
|
||||
}
|
||||
}
|
||||
fn cause(&self) -> Option<&StdError> {
|
||||
match *self {
|
||||
CharsError::NotUtf8 => None,
|
||||
CharsError::Other(ref e) => e.cause(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for CharsError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match *self {
|
||||
CharsError::NotUtf8 => {
|
||||
"byte stream did not contain valid utf8".fmt(f)
|
||||
}
|
||||
CharsError::Other(ref e) => e.fmt(f),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// An iterator over the contents of an instance of `BufRead` split on a
|
||||
/// particular byte.
|
||||
///
|
||||
/// See `BufReadExt::split` for more information.
|
||||
pub struct Split<B> {
|
||||
buf: B,
|
||||
delim: u8,
|
||||
}
|
||||
|
||||
impl<B: BufRead> Iterator for Split<B> {
|
||||
type Item = Result<Vec<u8>>;
|
||||
|
||||
fn next(&mut self) -> Option<Result<Vec<u8>>> {
|
||||
let mut buf = Vec::new();
|
||||
match self.buf.read_until(self.delim, &mut buf) {
|
||||
Ok(()) if buf.len() == 0 => None,
|
||||
Ok(()) => {
|
||||
if buf[buf.len() - 1] == self.delim {
|
||||
buf.pop();
|
||||
}
|
||||
Some(Ok(buf))
|
||||
}
|
||||
Err(e) => Some(Err(e))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// An iterator over the lines of an instance of `BufRead` split on a newline
|
||||
/// byte.
|
||||
///
|
||||
/// See `BufReadExt::lines` for more information.
|
||||
pub struct Lines<B> {
|
||||
buf: B,
|
||||
}
|
||||
|
||||
impl<B: BufRead> Iterator for Lines<B> {
|
||||
type Item = Result<String>;
|
||||
|
||||
fn next(&mut self) -> Option<Result<String>> {
|
||||
let mut buf = String::new();
|
||||
match self.buf.read_line(&mut buf) {
|
||||
Ok(()) if buf.len() == 0 => None,
|
||||
Ok(()) => {
|
||||
if buf.ends_with("\n") {
|
||||
buf.pop();
|
||||
}
|
||||
Some(Ok(buf))
|
||||
}
|
||||
Err(e) => Some(Err(e))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use prelude::v1::*;
|
||||
use io::prelude::*;
|
||||
use super::Cursor;
|
||||
|
||||
#[test]
|
||||
fn read_until() {
|
||||
let mut buf = Cursor::new(b"12");
|
||||
let mut v = Vec::new();
|
||||
assert_eq!(buf.read_until(b'3', &mut v), Ok(()));
|
||||
assert_eq!(v, b"12");
|
||||
|
||||
let mut buf = Cursor::new(b"1233");
|
||||
let mut v = Vec::new();
|
||||
assert_eq!(buf.read_until(b'3', &mut v), Ok(()));
|
||||
assert_eq!(v, b"123");
|
||||
v.truncate(0);
|
||||
assert_eq!(buf.read_until(b'3', &mut v), Ok(()));
|
||||
assert_eq!(v, b"3");
|
||||
v.truncate(0);
|
||||
assert_eq!(buf.read_until(b'3', &mut v), Ok(()));
|
||||
assert_eq!(v, []);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn split() {
|
||||
let mut buf = Cursor::new(b"12");
|
||||
let mut s = buf.split(b'3');
|
||||
assert_eq!(s.next(), Some(Ok(vec![b'1', b'2'])));
|
||||
assert_eq!(s.next(), None);
|
||||
|
||||
let mut buf = Cursor::new(b"1233");
|
||||
let mut s = buf.split(b'3');
|
||||
assert_eq!(s.next(), Some(Ok(vec![b'1', b'2'])));
|
||||
assert_eq!(s.next(), Some(Ok(vec![])));
|
||||
assert_eq!(s.next(), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn read_line() {
|
||||
let mut buf = Cursor::new(b"12");
|
||||
let mut v = String::new();
|
||||
assert_eq!(buf.read_line(&mut v), Ok(()));
|
||||
assert_eq!(v, "12");
|
||||
|
||||
let mut buf = Cursor::new(b"12\n\n");
|
||||
let mut v = String::new();
|
||||
assert_eq!(buf.read_line(&mut v), Ok(()));
|
||||
assert_eq!(v, "12\n");
|
||||
v.truncate(0);
|
||||
assert_eq!(buf.read_line(&mut v), Ok(()));
|
||||
assert_eq!(v, "\n");
|
||||
v.truncate(0);
|
||||
assert_eq!(buf.read_line(&mut v), Ok(()));
|
||||
assert_eq!(v, "");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn lines() {
|
||||
let mut buf = Cursor::new(b"12");
|
||||
let mut s = buf.lines();
|
||||
assert_eq!(s.next(), Some(Ok("12".to_string())));
|
||||
assert_eq!(s.next(), None);
|
||||
|
||||
let mut buf = Cursor::new(b"12\n\n");
|
||||
let mut s = buf.lines();
|
||||
assert_eq!(s.next(), Some(Ok("12".to_string())));
|
||||
assert_eq!(s.next(), Some(Ok(String::new())));
|
||||
assert_eq!(s.next(), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn read_to_end() {
|
||||
let mut c = Cursor::new(b"");
|
||||
let mut v = Vec::new();
|
||||
assert_eq!(c.read_to_end(&mut v), Ok(()));
|
||||
assert_eq!(v, []);
|
||||
|
||||
let mut c = Cursor::new(b"1");
|
||||
let mut v = Vec::new();
|
||||
assert_eq!(c.read_to_end(&mut v), Ok(()));
|
||||
assert_eq!(v, b"1");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn read_to_string() {
|
||||
let mut c = Cursor::new(b"");
|
||||
let mut v = String::new();
|
||||
assert_eq!(c.read_to_string(&mut v), Ok(()));
|
||||
assert_eq!(v, "");
|
||||
|
||||
let mut c = Cursor::new(b"1");
|
||||
let mut v = String::new();
|
||||
assert_eq!(c.read_to_string(&mut v), Ok(()));
|
||||
assert_eq!(v, "1");
|
||||
|
||||
let mut c = Cursor::new(b"\xff");
|
||||
let mut v = String::new();
|
||||
assert!(c.read_to_string(&mut v).is_err());
|
||||
}
|
||||
}
|
27
src/libstd/io/prelude.rs
Normal file
27
src/libstd/io/prelude.rs
Normal file
|
@ -0,0 +1,27 @@
|
|||
// Copyright 2015 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.
|
||||
|
||||
//! The I/O Prelude
|
||||
//!
|
||||
//! The purpose of this module is to alleviate imports of many common I/O traits
|
||||
//! by adding a glob import to the top of I/O heavy modules:
|
||||
//!
|
||||
//! ```
|
||||
//! use std::io::prelude::*;
|
||||
//! ```
|
||||
//!
|
||||
//! This module contains reexports of many core I/O traits such as `Read`,
|
||||
//! `Write`, `ReadExt`, and `WriteExt`. Structures and functions are not
|
||||
//! contained in this module.
|
||||
|
||||
pub use super::{Read, ReadExt, Write, WriteExt, BufRead, BufReadExt};
|
||||
|
||||
// FIXME: pub use as `Seek` when the name isn't in the actual prelude any more
|
||||
pub use super::Seek as NewSeek;
|
153
src/libstd/io/util.rs
Normal file
153
src/libstd/io/util.rs
Normal file
|
@ -0,0 +1,153 @@
|
|||
// Copyright 2014 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.
|
||||
|
||||
#![allow(missing_copy_implementations)]
|
||||
|
||||
use prelude::v1::*;
|
||||
|
||||
use io::{self, Read, Write, ErrorKind};
|
||||
|
||||
/// Copies the entire contents of a reader into a writer.
|
||||
///
|
||||
/// This function will continuously read data from `r` and then write it into
|
||||
/// `w` in a streaming fashion until `r` returns EOF.
|
||||
///
|
||||
/// On success the total number of bytes that were copied from `r` to `w` is
|
||||
/// returned.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// This function will return an error immediately if any call to `read` or
|
||||
/// `write` returns an error. All instances of `ErrorKind::Interrupted` are
|
||||
/// handled by this function and the underlying operation is retried.
|
||||
pub fn copy<R: Read, W: Write>(r: &mut R, w: &mut W) -> io::Result<u64> {
|
||||
let mut buf = [0; super::DEFAULT_BUF_SIZE];
|
||||
let mut written = 0;
|
||||
loop {
|
||||
let len = match r.read(&mut buf) {
|
||||
Ok(0) => return Ok(written),
|
||||
Ok(len) => len,
|
||||
Err(ref e) if e.kind() == ErrorKind::Interrupted => continue,
|
||||
Err(e) => return Err(e),
|
||||
};
|
||||
try!(w.write_all(&buf[..len]));
|
||||
written += len as u64;
|
||||
}
|
||||
}
|
||||
|
||||
/// A reader which is always at EOF.
|
||||
pub struct Empty { _priv: () }
|
||||
|
||||
/// Creates an instance of an empty reader.
|
||||
///
|
||||
/// All reads from the returned reader will return `Ok(0)`.
|
||||
pub fn empty() -> Empty { Empty { _priv: () } }
|
||||
|
||||
impl Read for Empty {
|
||||
fn read(&mut self, _buf: &mut [u8]) -> io::Result<usize> { Ok(0) }
|
||||
}
|
||||
|
||||
/// A reader which infinitely yields one byte.
|
||||
pub struct Repeat { byte: u8 }
|
||||
|
||||
/// Creates an instance of a reader that infinitely repeats one byte.
|
||||
///
|
||||
/// All reads from this reader will succeed by filling the specified buffer with
|
||||
/// the given byte.
|
||||
pub fn repeat(byte: u8) -> Repeat { Repeat { byte: byte } }
|
||||
|
||||
impl Read for Repeat {
|
||||
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
|
||||
for slot in buf.iter_mut() {
|
||||
*slot = self.byte;
|
||||
}
|
||||
Ok(buf.len())
|
||||
}
|
||||
}
|
||||
|
||||
/// A writer which will move data into the void.
|
||||
pub struct Sink { _priv: () }
|
||||
|
||||
/// Creates an instance of a writer which will successfully consume all data.
|
||||
///
|
||||
/// All calls to `write` on the returned instance will return `Ok(buf.len())`
|
||||
/// and the contents of the buffer will not be inspected.
|
||||
pub fn sink() -> Sink { Sink { _priv: () } }
|
||||
|
||||
impl Write for Sink {
|
||||
fn write(&mut self, buf: &[u8]) -> io::Result<usize> { Ok(buf.len()) }
|
||||
fn flush(&mut self) -> io::Result<()> { Ok(()) }
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use prelude::v1::*;
|
||||
|
||||
use io::prelude::*;
|
||||
use io::{sink, empty, repeat};
|
||||
|
||||
#[test]
|
||||
fn sink_sinks() {
|
||||
let mut s = sink();
|
||||
assert_eq!(s.write(&[]), Ok(0));
|
||||
assert_eq!(s.write(&[0]), Ok(1));
|
||||
assert_eq!(s.write(&[0; 1024]), Ok(1024));
|
||||
assert_eq!(s.by_ref().write(&[0; 1024]), Ok(1024));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn empty_reads() {
|
||||
let mut e = empty();
|
||||
assert_eq!(e.read(&mut []), Ok(0));
|
||||
assert_eq!(e.read(&mut [0]), Ok(0));
|
||||
assert_eq!(e.read(&mut [0; 1024]), Ok(0));
|
||||
assert_eq!(e.by_ref().read(&mut [0; 1024]), Ok(0));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn repeat_repeats() {
|
||||
let mut r = repeat(4);
|
||||
let mut b = [0; 1024];
|
||||
assert_eq!(r.read(&mut b), Ok(1024));
|
||||
assert!(b.iter().all(|b| *b == 4));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn take_some_bytes() {
|
||||
assert_eq!(repeat(4).take(100).bytes().count(), 100);
|
||||
assert_eq!(repeat(4).take(100).bytes().next(), Some(Ok(4)));
|
||||
assert_eq!(repeat(1).take(10).chain(repeat(2).take(10)).bytes().count(), 20);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn tee() {
|
||||
let mut buf = [0; 10];
|
||||
{
|
||||
let mut ptr: &mut [u8] = &mut buf;
|
||||
assert_eq!(repeat(4).tee(&mut ptr).take(5).read(&mut [0; 10]), Ok(5));
|
||||
}
|
||||
assert_eq!(buf, [4, 4, 4, 4, 4, 0, 0, 0, 0, 0]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn broadcast() {
|
||||
let mut buf1 = [0; 10];
|
||||
let mut buf2 = [0; 10];
|
||||
{
|
||||
let mut ptr1: &mut [u8] = &mut buf1;
|
||||
let mut ptr2: &mut [u8] = &mut buf2;
|
||||
|
||||
assert_eq!((&mut ptr1).broadcast(&mut ptr2)
|
||||
.write(&[1, 2, 3]), Ok(3));
|
||||
}
|
||||
assert_eq!(buf1, buf2);
|
||||
assert_eq!(buf1, [1, 2, 3, 0, 0, 0, 0, 0, 0, 0]);
|
||||
}
|
||||
}
|
|
@ -128,9 +128,8 @@
|
|||
|
||||
#![deny(missing_docs)]
|
||||
|
||||
#[cfg(test)]
|
||||
#[macro_use]
|
||||
extern crate log;
|
||||
#[cfg(test)] extern crate test;
|
||||
#[cfg(test)] #[macro_use] extern crate log;
|
||||
|
||||
#[macro_use]
|
||||
#[macro_reexport(assert, assert_eq, debug_assert, debug_assert_eq,
|
||||
|
@ -248,9 +247,11 @@ pub mod dynamic_lib;
|
|||
pub mod ffi;
|
||||
pub mod fmt;
|
||||
pub mod old_io;
|
||||
pub mod io;
|
||||
pub mod os;
|
||||
pub mod env;
|
||||
pub mod path;
|
||||
pub mod old_path;
|
||||
pub mod rand;
|
||||
pub mod time;
|
||||
|
||||
|
|
|
@ -61,8 +61,8 @@ use old_io;
|
|||
use iter::{Iterator, Extend};
|
||||
use option::Option;
|
||||
use option::Option::{Some, None};
|
||||
use path::{Path, GenericPath};
|
||||
use path;
|
||||
use old_path::{Path, GenericPath};
|
||||
use old_path;
|
||||
use result::Result::{Err, Ok};
|
||||
use slice::SliceExt;
|
||||
use string::String;
|
||||
|
@ -782,7 +782,7 @@ pub trait PathExtensions {
|
|||
fn is_dir(&self) -> bool;
|
||||
}
|
||||
|
||||
impl PathExtensions for path::Path {
|
||||
impl PathExtensions for old_path::Path {
|
||||
fn stat(&self) -> IoResult<FileStat> { stat(self) }
|
||||
fn lstat(&self) -> IoResult<FileStat> { lstat(self) }
|
||||
fn exists(&self) -> bool {
|
||||
|
|
|
@ -23,7 +23,7 @@
|
|||
use prelude::v1::*;
|
||||
|
||||
use ffi::CString;
|
||||
use path::BytesContainer;
|
||||
use old_path::BytesContainer;
|
||||
use old_io::{Listener, Acceptor, IoResult, TimedOut, standard_error};
|
||||
use sys::pipe::UnixAcceptor as UnixAcceptorImp;
|
||||
use sys::pipe::UnixListener as UnixListenerImp;
|
||||
|
|
|
@ -25,7 +25,7 @@ use old_io::{IoResult, IoError};
|
|||
use old_io;
|
||||
use libc;
|
||||
use os;
|
||||
use path::BytesContainer;
|
||||
use old_path::BytesContainer;
|
||||
use sync::mpsc::{channel, Receiver};
|
||||
use sys::fs::FileDesc;
|
||||
use sys::process::Process as ProcessImp;
|
||||
|
|
|
@ -17,7 +17,7 @@ use old_io;
|
|||
use ops::Drop;
|
||||
use option::Option::{None, Some};
|
||||
use option::Option;
|
||||
use path::{Path, GenericPath};
|
||||
use old_path::{Path, GenericPath};
|
||||
use rand::{Rng, thread_rng};
|
||||
use result::Result::{Ok, Err};
|
||||
use str::StrExt;
|
||||
|
|
|
@ -445,7 +445,7 @@ mod tests {
|
|||
use clone::Clone;
|
||||
use iter::IteratorExt;
|
||||
use option::Option::{self, Some, None};
|
||||
use path::GenericPath;
|
||||
use old_path::GenericPath;
|
||||
use slice::{AsSlice, SliceExt};
|
||||
use str::{self, Str, StrExt};
|
||||
use string::ToString;
|
|
@ -1124,7 +1124,7 @@ mod tests {
|
|||
use clone::Clone;
|
||||
use iter::IteratorExt;
|
||||
use option::Option::{self, Some, None};
|
||||
use path::GenericPath;
|
||||
use old_path::GenericPath;
|
||||
use slice::{AsSlice, SliceExt};
|
||||
use str::Str;
|
||||
use string::ToString;
|
|
@ -48,7 +48,7 @@ use old_io::{IoResult, IoError};
|
|||
use ops::{Drop, FnOnce};
|
||||
use option::Option::{Some, None};
|
||||
use option::Option;
|
||||
use path::{Path, GenericPath, BytesContainer};
|
||||
use old_path::{Path, GenericPath, BytesContainer};
|
||||
use ptr::PtrExt;
|
||||
use ptr;
|
||||
use result::Result::{Err, Ok};
|
||||
|
@ -267,7 +267,7 @@ pub fn split_paths<T: BytesContainer>(unparsed: T) -> Vec<Path> {
|
|||
///
|
||||
/// ```rust
|
||||
/// use std::os;
|
||||
/// use std::path::Path;
|
||||
/// use std::old_path::Path;
|
||||
///
|
||||
/// let key = "PATH";
|
||||
/// let mut paths = os::getenv_as_bytes(key).map_or(Vec::new(), os::split_paths);
|
||||
|
@ -470,7 +470,7 @@ pub fn tmpdir() -> Path {
|
|||
/// # Example
|
||||
/// ```rust
|
||||
/// use std::os;
|
||||
/// use std::path::Path;
|
||||
/// use std::old_path::Path;
|
||||
///
|
||||
/// // Assume we're in a path like /home/someuser
|
||||
/// let rel_path = Path::new("..");
|
||||
|
@ -500,7 +500,7 @@ pub fn make_absolute(p: &Path) -> IoResult<Path> {
|
|||
/// # Example
|
||||
/// ```rust
|
||||
/// use std::os;
|
||||
/// use std::path::Path;
|
||||
/// use std::old_path::Path;
|
||||
///
|
||||
/// let root = Path::new("/");
|
||||
/// assert!(os::change_dir(&root).is_ok());
|
||||
|
|
2577
src/libstd/path.rs
Executable file
2577
src/libstd/path.rs
Executable file
File diff suppressed because it is too large
Load diff
|
@ -56,7 +56,7 @@
|
|||
#[doc(no_inline)] pub use vec::Vec;
|
||||
|
||||
// NB: remove when path reform lands
|
||||
#[doc(no_inline)] pub use path::{Path, GenericPath};
|
||||
#[doc(no_inline)] pub use old_path::{Path, GenericPath};
|
||||
// NB: remove when I/O reform lands
|
||||
#[doc(no_inline)] pub use old_io::{Buffer, Writer, Reader, Seek, BufferPrelude};
|
||||
// NB: remove when range syntax lands
|
||||
|
|
|
@ -20,7 +20,7 @@ mod imp {
|
|||
use self::OsRngInner::*;
|
||||
|
||||
use old_io::{IoResult, File};
|
||||
use path::Path;
|
||||
use old_path::Path;
|
||||
use rand::Rng;
|
||||
use rand::reader::ReaderRng;
|
||||
use result::Result::Ok;
|
||||
|
|
|
@ -16,7 +16,7 @@ use prelude::v1::*;
|
|||
use sys::{last_error, retry};
|
||||
use ffi::CString;
|
||||
use num::Int;
|
||||
use path::BytesContainer;
|
||||
use old_path::BytesContainer;
|
||||
use collections;
|
||||
|
||||
pub mod backtrace;
|
||||
|
|
|
@ -143,6 +143,7 @@ extern {
|
|||
pub fn sigdelset(set: *mut sigset_t, signum: libc::c_int) -> libc::c_int;
|
||||
pub fn sigemptyset(set: *mut sigset_t) -> libc::c_int;
|
||||
|
||||
#[cfg(not(target_os = "ios"))]
|
||||
pub fn getpwuid_r(uid: libc::uid_t,
|
||||
pwd: *mut passwd,
|
||||
buf: *mut libc::c_char,
|
||||
|
|
|
@ -18,10 +18,11 @@
|
|||
use prelude::v1::*;
|
||||
|
||||
use ffi;
|
||||
use old_io::{self, IoResult, IoError};
|
||||
use io::ErrorKind;
|
||||
use libc;
|
||||
use num::{Int, SignedInt};
|
||||
use num;
|
||||
use old_io::{self, IoResult, IoError};
|
||||
use str;
|
||||
use sys_common::mkerr_libc;
|
||||
|
||||
|
@ -133,6 +134,35 @@ pub fn decode_error_detailed(errno: i32) -> IoError {
|
|||
err
|
||||
}
|
||||
|
||||
pub fn decode_error_kind(errno: i32) -> ErrorKind {
|
||||
match errno as libc::c_int {
|
||||
libc::ECONNREFUSED => ErrorKind::ConnectionRefused,
|
||||
libc::ECONNRESET => ErrorKind::ConnectionReset,
|
||||
libc::EPERM | libc::EACCES => ErrorKind::PermissionDenied,
|
||||
libc::EPIPE => ErrorKind::BrokenPipe,
|
||||
libc::ENOTCONN => ErrorKind::NotConnected,
|
||||
libc::ECONNABORTED => ErrorKind::ConnectionAborted,
|
||||
libc::EADDRNOTAVAIL => ErrorKind::ConnectionRefused,
|
||||
libc::EADDRINUSE => ErrorKind::ConnectionRefused,
|
||||
libc::ENOENT => ErrorKind::FileNotFound,
|
||||
libc::EISDIR => ErrorKind::InvalidInput,
|
||||
libc::EINTR => ErrorKind::Interrupted,
|
||||
libc::EINVAL => ErrorKind::InvalidInput,
|
||||
libc::ENOTTY => ErrorKind::MismatchedFileTypeForOperation,
|
||||
libc::ETIMEDOUT => ErrorKind::TimedOut,
|
||||
libc::ECANCELED => ErrorKind::TimedOut,
|
||||
libc::consts::os::posix88::EEXIST => ErrorKind::PathAlreadyExists,
|
||||
|
||||
// These two constants can have the same value on some systems,
|
||||
// but different values on others, so we can't use a match
|
||||
// clause
|
||||
x if x == libc::EAGAIN || x == libc::EWOULDBLOCK =>
|
||||
ErrorKind::ResourceUnavailable,
|
||||
|
||||
_ => ErrorKind::Other,
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn retry<T, F> (mut f: F) -> T where
|
||||
T: SignedInt,
|
||||
|
|
|
@ -307,23 +307,23 @@ pub fn args() -> Args {
|
|||
let mut res = Vec::new();
|
||||
|
||||
unsafe {
|
||||
let processInfoSel = sel_registerName("processInfo\0".as_ptr());
|
||||
let argumentsSel = sel_registerName("arguments\0".as_ptr());
|
||||
let utf8Sel = sel_registerName("UTF8String\0".as_ptr());
|
||||
let countSel = sel_registerName("count\0".as_ptr());
|
||||
let objectAtSel = sel_registerName("objectAtIndex:\0".as_ptr());
|
||||
let process_info_sel = sel_registerName("processInfo\0".as_ptr());
|
||||
let arguments_sel = sel_registerName("arguments\0".as_ptr());
|
||||
let utf8_sel = sel_registerName("UTF8String\0".as_ptr());
|
||||
let count_sel = sel_registerName("count\0".as_ptr());
|
||||
let object_at_sel = sel_registerName("objectAtIndex:\0".as_ptr());
|
||||
|
||||
let klass = objc_getClass("NSProcessInfo\0".as_ptr());
|
||||
let info = objc_msgSend(klass, processInfoSel);
|
||||
let args = objc_msgSend(info, argumentsSel);
|
||||
let info = objc_msgSend(klass, process_info_sel);
|
||||
let args = objc_msgSend(info, arguments_sel);
|
||||
|
||||
let cnt: int = mem::transmute(objc_msgSend(args, countSel));
|
||||
let cnt: int = mem::transmute(objc_msgSend(args, count_sel));
|
||||
for i in range(0, cnt) {
|
||||
let tmp = objc_msgSend(args, objectAtSel, i);
|
||||
let tmp = objc_msgSend(args, object_at_sel, i);
|
||||
let utf_c_str: *const libc::c_char =
|
||||
mem::transmute(objc_msgSend(tmp, utf8Sel));
|
||||
let bytes = ffi::c_str_to_bytes(&utf_c_str).to_vec();
|
||||
res.push(OsString::from_vec(bytes))
|
||||
mem::transmute(objc_msgSend(tmp, utf8_sel));
|
||||
let bytes = ffi::c_str_to_bytes(&utf_c_str);
|
||||
res.push(OsString::from_str(str::from_utf8(bytes).unwrap()))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -455,9 +455,11 @@ pub fn home_dir() -> Option<Path> {
|
|||
Path::new(os.into_vec())
|
||||
});
|
||||
|
||||
#[cfg(target_os = "android")]
|
||||
#[cfg(any(target_os = "android",
|
||||
target_os = "ios"))]
|
||||
unsafe fn fallback() -> Option<OsString> { None }
|
||||
#[cfg(not(target_os = "android"))]
|
||||
#[cfg(not(any(target_os = "android",
|
||||
target_os = "ios")))]
|
||||
unsafe fn fallback() -> Option<OsString> {
|
||||
let mut amt = match libc::sysconf(c::_SC_GETPW_R_SIZE_MAX) {
|
||||
n if n < 0 => 512 as usize,
|
||||
|
|
|
@ -20,7 +20,7 @@ use str;
|
|||
use string::{String, CowString};
|
||||
use mem;
|
||||
|
||||
#[derive(Clone)]
|
||||
#[derive(Clone, Hash)]
|
||||
pub struct Buf {
|
||||
pub inner: Vec<u8>
|
||||
}
|
||||
|
|
|
@ -20,7 +20,7 @@ use old_io::{self, IoResult, IoError, EndOfFile};
|
|||
use libc::{self, pid_t, c_void, c_int};
|
||||
use mem;
|
||||
use os;
|
||||
use path::BytesContainer;
|
||||
use old_path::BytesContainer;
|
||||
use ptr;
|
||||
use sync::mpsc::{channel, Sender, Receiver};
|
||||
use sys::fs::FileDesc;
|
||||
|
|
|
@ -32,7 +32,7 @@ use libc;
|
|||
use mem;
|
||||
use ops::Drop;
|
||||
use option::Option::{Some};
|
||||
use path::Path;
|
||||
use old_path::Path;
|
||||
use ptr;
|
||||
use result::Result::{Ok, Err};
|
||||
use slice::SliceExt;
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
use prelude::v1::*;
|
||||
|
||||
use ffi::OsStr;
|
||||
use io::ErrorKind;
|
||||
use libc;
|
||||
use mem;
|
||||
use old_io::{self, IoResult, IoError};
|
||||
|
@ -143,6 +144,34 @@ pub fn decode_error_detailed(errno: i32) -> IoError {
|
|||
err
|
||||
}
|
||||
|
||||
pub fn decode_error_kind(errno: i32) -> ErrorKind {
|
||||
match errno as libc::c_int {
|
||||
libc::ERROR_ACCESS_DENIED => ErrorKind::PermissionDenied,
|
||||
libc::ERROR_ALREADY_EXISTS => ErrorKind::PathAlreadyExists,
|
||||
libc::ERROR_BROKEN_PIPE => ErrorKind::BrokenPipe,
|
||||
libc::ERROR_FILE_NOT_FOUND => ErrorKind::FileNotFound,
|
||||
libc::ERROR_INVALID_FUNCTION => ErrorKind::InvalidInput,
|
||||
libc::ERROR_INVALID_HANDLE => ErrorKind::MismatchedFileTypeForOperation,
|
||||
libc::ERROR_INVALID_NAME => ErrorKind::InvalidInput,
|
||||
libc::ERROR_NOTHING_TO_TERMINATE => ErrorKind::InvalidInput,
|
||||
libc::ERROR_NO_DATA => ErrorKind::BrokenPipe,
|
||||
libc::ERROR_OPERATION_ABORTED => ErrorKind::TimedOut,
|
||||
|
||||
libc::WSAEACCES => ErrorKind::PermissionDenied,
|
||||
libc::WSAEADDRINUSE => ErrorKind::ConnectionRefused,
|
||||
libc::WSAEADDRNOTAVAIL => ErrorKind::ConnectionRefused,
|
||||
libc::WSAECONNABORTED => ErrorKind::ConnectionAborted,
|
||||
libc::WSAECONNREFUSED => ErrorKind::ConnectionRefused,
|
||||
libc::WSAECONNRESET => ErrorKind::ConnectionReset,
|
||||
libc::WSAEINVAL => ErrorKind::InvalidInput,
|
||||
libc::WSAENOTCONN => ErrorKind::NotConnected,
|
||||
libc::WSAEWOULDBLOCK => ErrorKind::ResourceUnavailable,
|
||||
|
||||
_ => ErrorKind::Other,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[inline]
|
||||
pub fn retry<I, F>(f: F) -> I where F: FnOnce() -> I { f() } // PR rust-lang/rust/#17020
|
||||
|
||||
|
|
|
@ -18,7 +18,7 @@ use result::Result;
|
|||
use option::Option;
|
||||
use mem;
|
||||
|
||||
#[derive(Clone)]
|
||||
#[derive(Clone, Hash)]
|
||||
pub struct Buf {
|
||||
pub inner: Wtf8Buf
|
||||
}
|
||||
|
|
|
@ -23,7 +23,7 @@ use old_io::process::{ProcessExit, ExitStatus};
|
|||
use old_io::{IoResult, IoError};
|
||||
use old_io;
|
||||
use os;
|
||||
use path::BytesContainer;
|
||||
use old_path::BytesContainer;
|
||||
use ptr;
|
||||
use str;
|
||||
use sync::{StaticMutex, MUTEX_INIT};
|
||||
|
|
|
@ -45,6 +45,7 @@ pub mod scoped;
|
|||
|
||||
// Sure wish we had macro hygiene, no?
|
||||
#[doc(hidden)]
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
pub mod __impl {
|
||||
pub use super::imp::Key as KeyInner;
|
||||
pub use super::imp::destroy_value;
|
||||
|
|
|
@ -48,7 +48,6 @@ pub use self::TraitItem::*;
|
|||
pub use self::Ty_::*;
|
||||
pub use self::TyParamBound::*;
|
||||
pub use self::UintTy::*;
|
||||
pub use self::ClosureKind::*;
|
||||
pub use self::UnOp::*;
|
||||
pub use self::UnsafeSource::*;
|
||||
pub use self::VariantKind::*;
|
||||
|
@ -736,7 +735,7 @@ pub enum Expr_ {
|
|||
// FIXME #6993: change to Option<Name> ... or not, if these are hygienic.
|
||||
ExprLoop(P<Block>, Option<Ident>),
|
||||
ExprMatch(P<Expr>, Vec<Arm>, MatchSource),
|
||||
ExprClosure(CaptureClause, Option<ClosureKind>, P<FnDecl>, P<Block>),
|
||||
ExprClosure(CaptureClause, P<FnDecl>, P<Block>),
|
||||
ExprBlock(P<Block>),
|
||||
|
||||
ExprAssign(P<Expr>, P<Expr>),
|
||||
|
@ -1687,13 +1686,6 @@ impl ForeignItem_ {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug, Copy)]
|
||||
pub enum ClosureKind {
|
||||
FnClosureKind,
|
||||
FnMutClosureKind,
|
||||
FnOnceClosureKind,
|
||||
}
|
||||
|
||||
/// The data we save and restore about an inlined item or method. This is not
|
||||
/// part of the AST that we parse from a file, but it becomes part of the tree
|
||||
/// that we trans.
|
||||
|
|
|
@ -218,7 +218,7 @@ impl<'a> FnLikeNode<'a> {
|
|||
}
|
||||
}
|
||||
ast_map::NodeExpr(e) => match e.node {
|
||||
ast::ExprClosure(_, _, ref decl, ref block) =>
|
||||
ast::ExprClosure(_, ref decl, ref block) =>
|
||||
closure(ClosureParts::new(&**decl, &**block, e.id, e.span)),
|
||||
_ => panic!("expr FnLikeNode that is not fn-like"),
|
||||
},
|
||||
|
|
|
@ -876,14 +876,14 @@ impl<'a> AstBuilder for ExtCtxt<'a> {
|
|||
|
||||
fn lambda_fn_decl(&self, span: Span,
|
||||
fn_decl: P<ast::FnDecl>, blk: P<ast::Block>) -> P<ast::Expr> {
|
||||
self.expr(span, ast::ExprClosure(ast::CaptureByRef, None, fn_decl, blk))
|
||||
self.expr(span, ast::ExprClosure(ast::CaptureByRef, fn_decl, blk))
|
||||
}
|
||||
fn lambda(&self, span: Span, ids: Vec<ast::Ident>, blk: P<ast::Block>) -> P<ast::Expr> {
|
||||
let fn_decl = self.fn_decl(
|
||||
ids.iter().map(|id| self.arg(span, *id, self.ty_infer(span))).collect(),
|
||||
self.ty_infer(span));
|
||||
|
||||
self.expr(span, ast::ExprClosure(ast::CaptureByRef, None, fn_decl, blk))
|
||||
self.expr(span, ast::ExprClosure(ast::CaptureByRef, fn_decl, blk))
|
||||
}
|
||||
fn lambda0(&self, span: Span, blk: P<ast::Block>) -> P<ast::Expr> {
|
||||
self.lambda(span, Vec::new(), blk)
|
||||
|
|
|
@ -66,7 +66,7 @@ fn rand_substructure(cx: &mut ExtCtxt, trait_span: Span, substr: &Substructure)
|
|||
cx.ident_of("Rand"),
|
||||
cx.ident_of("rand")
|
||||
);
|
||||
let mut rand_call = |&mut: cx: &mut ExtCtxt, span| {
|
||||
let rand_call = |&: cx: &mut ExtCtxt, span| {
|
||||
cx.expr_call_global(span,
|
||||
rand_ident.clone(),
|
||||
vec!(rng.clone()))
|
||||
|
|
|
@ -322,11 +322,10 @@ pub fn expand_expr(e: P<ast::Expr>, fld: &mut MacroExpander) -> P<ast::Expr> {
|
|||
fld.cx.expr_match(span, into_iter_expr, vec![iter_arm])
|
||||
}
|
||||
|
||||
ast::ExprClosure(capture_clause, opt_kind, fn_decl, block) => {
|
||||
ast::ExprClosure(capture_clause, fn_decl, block) => {
|
||||
let (rewritten_fn_decl, rewritten_block)
|
||||
= expand_and_rename_fn_decl_and_block(fn_decl, block, fld);
|
||||
let new_node = ast::ExprClosure(capture_clause,
|
||||
opt_kind,
|
||||
rewritten_fn_decl,
|
||||
rewritten_block);
|
||||
P(ast::Expr{id:id, node: new_node, span: fld.new_span(span)})
|
||||
|
|
|
@ -119,6 +119,9 @@ static KNOWN_FEATURES: &'static [(&'static str, &'static str, Status)] = &[
|
|||
|
||||
// Allows use of #[staged_api]
|
||||
("staged_api", "1.0.0", Active),
|
||||
|
||||
// Allows using items which are missing stability attributes
|
||||
("unmarked_api", "1.0.0", Active)
|
||||
];
|
||||
|
||||
enum Status {
|
||||
|
@ -145,6 +148,7 @@ pub struct Features {
|
|||
pub quote: bool,
|
||||
pub old_orphan_check: bool,
|
||||
pub simd_ffi: bool,
|
||||
pub unmarked_api: bool,
|
||||
pub lib_features: Vec<(InternedString, Span)>
|
||||
}
|
||||
|
||||
|
@ -157,6 +161,7 @@ impl Features {
|
|||
quote: false,
|
||||
old_orphan_check: false,
|
||||
simd_ffi: false,
|
||||
unmarked_api: false,
|
||||
lib_features: Vec::new()
|
||||
}
|
||||
}
|
||||
|
@ -566,6 +571,7 @@ fn check_crate_inner<F>(cm: &CodeMap, span_handler: &SpanHandler, krate: &ast::C
|
|||
quote: cx.has_feature("quote"),
|
||||
old_orphan_check: cx.has_feature("old_orphan_check"),
|
||||
simd_ffi: cx.has_feature("simd_ffi"),
|
||||
unmarked_api: cx.has_feature("unmarked_api"),
|
||||
lib_features: unknown_features
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1325,9 +1325,8 @@ pub fn noop_fold_expr<T: Folder>(Expr {id, node, span}: Expr, folder: &mut T) ->
|
|||
arms.move_map(|x| folder.fold_arm(x)),
|
||||
source)
|
||||
}
|
||||
ExprClosure(capture_clause, opt_kind, decl, body) => {
|
||||
ExprClosure(capture_clause, decl, body) => {
|
||||
ExprClosure(capture_clause,
|
||||
opt_kind,
|
||||
folder.fold_fn_decl(decl),
|
||||
folder.fold_block(body))
|
||||
}
|
||||
|
|
|
@ -27,6 +27,7 @@ pub enum ObsoleteSyntax {
|
|||
ProcType,
|
||||
ProcExpr,
|
||||
ClosureType,
|
||||
ClosureKind,
|
||||
}
|
||||
|
||||
pub trait ParserObsoleteMethods {
|
||||
|
@ -65,6 +66,10 @@ impl<'a> ParserObsoleteMethods for parser::Parser<'a> {
|
|||
"`|usize| -> bool` closure type syntax",
|
||||
"use unboxed closures instead, no type annotation needed"
|
||||
),
|
||||
ObsoleteSyntax::ClosureKind => (
|
||||
"`:`, `&mut:`, or `&:` syntax",
|
||||
"rely on inference instead"
|
||||
),
|
||||
ObsoleteSyntax::Sized => (
|
||||
"`Sized? T` syntax for removing the `Sized` bound",
|
||||
"write `T: ?Sized` instead"
|
||||
|
|
|
@ -28,8 +28,6 @@ use ast::{ExprLit, ExprLoop, ExprMac, ExprRange};
|
|||
use ast::{ExprMethodCall, ExprParen, ExprPath, ExprQPath};
|
||||
use ast::{ExprRepeat, ExprRet, ExprStruct, ExprTup, ExprUnary};
|
||||
use ast::{ExprVec, ExprWhile, ExprWhileLet, ExprForLoop, Field, FnDecl};
|
||||
use ast::{FnClosureKind, FnMutClosureKind};
|
||||
use ast::{FnOnceClosureKind};
|
||||
use ast::{ForeignItem, ForeignItemStatic, ForeignItemFn, ForeignMod, FunctionRetTy};
|
||||
use ast::{Ident, Inherited, ImplItem, Item, Item_, ItemStatic};
|
||||
use ast::{ItemEnum, ItemFn, ItemForeignMod, ItemImpl, ItemConst};
|
||||
|
@ -57,7 +55,7 @@ use ast::{TyFixedLengthVec, TyBareFn};
|
|||
use ast::{TyTypeof, TyInfer, TypeMethod};
|
||||
use ast::{TyParam, TyParamBound, TyParen, TyPath, TyPolyTraitRef, TyPtr, TyQPath};
|
||||
use ast::{TyRptr, TyTup, TyU32, TyVec, UnUniq};
|
||||
use ast::{TypeImplItem, TypeTraitItem, Typedef, ClosureKind};
|
||||
use ast::{TypeImplItem, TypeTraitItem, Typedef,};
|
||||
use ast::{UnnamedField, UnsafeBlock};
|
||||
use ast::{ViewPath, ViewPathGlob, ViewPathList, ViewPathSimple};
|
||||
use ast::{Visibility, WhereClause};
|
||||
|
@ -1139,29 +1137,36 @@ impl<'a> Parser<'a> {
|
|||
TyInfer
|
||||
}
|
||||
|
||||
/// Parses an optional closure kind (`&:`, `&mut:`, or `:`).
|
||||
pub fn parse_optional_closure_kind(&mut self) -> Option<ClosureKind> {
|
||||
if self.check(&token::BinOp(token::And)) &&
|
||||
self.look_ahead(1, |t| t.is_keyword(keywords::Mut)) &&
|
||||
self.look_ahead(2, |t| *t == token::Colon) {
|
||||
/// Parses an obsolete closure kind (`&:`, `&mut:`, or `:`).
|
||||
pub fn parse_obsolete_closure_kind(&mut self) {
|
||||
// let lo = self.span.lo;
|
||||
if
|
||||
self.check(&token::BinOp(token::And)) &&
|
||||
self.look_ahead(1, |t| t.is_keyword(keywords::Mut)) &&
|
||||
self.look_ahead(2, |t| *t == token::Colon)
|
||||
{
|
||||
self.bump();
|
||||
self.bump();
|
||||
self.bump();
|
||||
return Some(FnMutClosureKind)
|
||||
} else if
|
||||
self.token == token::BinOp(token::And) &&
|
||||
self.look_ahead(1, |t| *t == token::Colon)
|
||||
{
|
||||
self.bump();
|
||||
self.bump();
|
||||
return;
|
||||
} else if
|
||||
self.eat(&token::Colon)
|
||||
{
|
||||
/* nothing */
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
|
||||
if self.token == token::BinOp(token::And) &&
|
||||
self.look_ahead(1, |t| *t == token::Colon) {
|
||||
self.bump();
|
||||
self.bump();
|
||||
return Some(FnClosureKind)
|
||||
}
|
||||
|
||||
if self.eat(&token::Colon) {
|
||||
return Some(FnOnceClosureKind)
|
||||
}
|
||||
|
||||
return None
|
||||
// SNAP 474b324
|
||||
// Enable these obsolete errors after snapshot:
|
||||
// let span = mk_sp(lo, self.span.hi);
|
||||
// self.obsolete(span, ObsoleteSyntax::ClosureKind);
|
||||
}
|
||||
|
||||
pub fn parse_ty_bare_fn_or_ty_closure(&mut self, lifetime_defs: Vec<LifetimeDef>) -> Ty_ {
|
||||
|
@ -3047,7 +3052,7 @@ impl<'a> Parser<'a> {
|
|||
-> P<Expr>
|
||||
{
|
||||
let lo = self.span.lo;
|
||||
let (decl, optional_closure_kind) = self.parse_fn_block_decl();
|
||||
let decl = self.parse_fn_block_decl();
|
||||
let body = self.parse_expr();
|
||||
let fakeblock = P(ast::Block {
|
||||
id: ast::DUMMY_NODE_ID,
|
||||
|
@ -3060,7 +3065,7 @@ impl<'a> Parser<'a> {
|
|||
self.mk_expr(
|
||||
lo,
|
||||
fakeblock.span.hi,
|
||||
ExprClosure(capture_clause, optional_closure_kind, decl, fakeblock))
|
||||
ExprClosure(capture_clause, decl, fakeblock))
|
||||
}
|
||||
|
||||
pub fn parse_else_expr(&mut self) -> P<Expr> {
|
||||
|
@ -4529,30 +4534,29 @@ impl<'a> Parser<'a> {
|
|||
}
|
||||
|
||||
// parse the |arg, arg| header on a lambda
|
||||
fn parse_fn_block_decl(&mut self) -> (P<FnDecl>, Option<ClosureKind>) {
|
||||
let (optional_closure_kind, inputs_captures) = {
|
||||
fn parse_fn_block_decl(&mut self) -> P<FnDecl> {
|
||||
let inputs_captures = {
|
||||
if self.eat(&token::OrOr) {
|
||||
(None, Vec::new())
|
||||
Vec::new()
|
||||
} else {
|
||||
self.expect(&token::BinOp(token::Or));
|
||||
let optional_closure_kind =
|
||||
self.parse_optional_closure_kind();
|
||||
self.parse_obsolete_closure_kind();
|
||||
let args = self.parse_seq_to_before_end(
|
||||
&token::BinOp(token::Or),
|
||||
seq_sep_trailing_allowed(token::Comma),
|
||||
|p| p.parse_fn_block_arg()
|
||||
);
|
||||
self.bump();
|
||||
(optional_closure_kind, args)
|
||||
args
|
||||
}
|
||||
};
|
||||
let output = self.parse_ret_ty();
|
||||
|
||||
(P(FnDecl {
|
||||
P(FnDecl {
|
||||
inputs: inputs_captures,
|
||||
output: output,
|
||||
variadic: false
|
||||
}), optional_closure_kind)
|
||||
})
|
||||
}
|
||||
|
||||
/// Parses the `(arg, arg) -> return_type` header on a procedure.
|
||||
|
|
|
@ -25,7 +25,7 @@ use serialize::{Decodable, Decoder, Encodable, Encoder};
|
|||
use std::fmt;
|
||||
use std::mem;
|
||||
use std::ops::Deref;
|
||||
use std::path::BytesContainer;
|
||||
use std::old_path::BytesContainer;
|
||||
use std::rc::Rc;
|
||||
|
||||
#[allow(non_camel_case_types)]
|
||||
|
|
|
@ -11,11 +11,9 @@
|
|||
pub use self::AnnNode::*;
|
||||
|
||||
use abi;
|
||||
use ast::{self, FnClosureKind, FnMutClosureKind};
|
||||
use ast::{FnOnceClosureKind};
|
||||
use ast;
|
||||
use ast::{MethodImplItem, RegionTyParamBound, TraitTyParamBound, TraitBoundModifier};
|
||||
use ast::{RequiredMethod, ProvidedMethod, TypeImplItem, TypeTraitItem};
|
||||
use ast::{ClosureKind};
|
||||
use ast_util;
|
||||
use owned_slice::OwnedSlice;
|
||||
use attr::{AttrMetaMethods, AttributeMethods};
|
||||
|
@ -350,7 +348,7 @@ pub fn method_to_string(p: &ast::Method) -> String {
|
|||
}
|
||||
|
||||
pub fn fn_block_to_string(p: &ast::FnDecl) -> String {
|
||||
$to_string(|s| s.print_fn_block_args(p, None))
|
||||
$to_string(|s| s.print_fn_block_args(p))
|
||||
}
|
||||
|
||||
pub fn path_to_string(p: &ast::Path) -> String {
|
||||
|
@ -1747,10 +1745,10 @@ impl<'a> State<'a> {
|
|||
}
|
||||
try!(self.bclose_(expr.span, indent_unit));
|
||||
}
|
||||
ast::ExprClosure(capture_clause, opt_kind, ref decl, ref body) => {
|
||||
ast::ExprClosure(capture_clause, ref decl, ref body) => {
|
||||
try!(self.print_capture_clause(capture_clause));
|
||||
|
||||
try!(self.print_fn_block_args(&**decl, opt_kind));
|
||||
try!(self.print_fn_block_args(&**decl));
|
||||
try!(space(&mut self.s));
|
||||
|
||||
if !body.stmts.is_empty() || !body.expr.is_some() {
|
||||
|
@ -2350,16 +2348,9 @@ impl<'a> State<'a> {
|
|||
|
||||
pub fn print_fn_block_args(
|
||||
&mut self,
|
||||
decl: &ast::FnDecl,
|
||||
closure_kind: Option<ClosureKind>)
|
||||
decl: &ast::FnDecl)
|
||||
-> IoResult<()> {
|
||||
try!(word(&mut self.s, "|"));
|
||||
match closure_kind {
|
||||
None => {}
|
||||
Some(FnClosureKind) => try!(self.word_space("&:")),
|
||||
Some(FnMutClosureKind) => try!(self.word_space("&mut:")),
|
||||
Some(FnOnceClosureKind) => try!(self.word_space(":")),
|
||||
}
|
||||
try!(self.print_fn_args(decl, None));
|
||||
try!(word(&mut self.s, "|"));
|
||||
|
||||
|
|
|
@ -836,7 +836,7 @@ pub fn walk_expr<'v, V: Visitor<'v>>(visitor: &mut V, expression: &'v Expr) {
|
|||
visitor.visit_arm(arm)
|
||||
}
|
||||
}
|
||||
ExprClosure(_, _, ref function_declaration, ref body) => {
|
||||
ExprClosure(_, ref function_declaration, ref body) => {
|
||||
visitor.visit_fn(FkFnBlock,
|
||||
&**function_declaration,
|
||||
&**body,
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
#![crate_type = "lib"]
|
||||
#![feature(staged_api)]
|
||||
#![staged_api]
|
||||
#![stable(feature = "lint_stability", since = "1.0.0")]
|
||||
|
||||
#[stable(feature = "test_feature", since = "1.0.0")]
|
||||
#[deprecated(since = "1.0.0")]
|
||||
|
@ -31,8 +32,6 @@ pub fn unstable() {}
|
|||
#[unstable(feature = "test_feature", reason = "text")]
|
||||
pub fn unstable_text() {}
|
||||
|
||||
pub fn unmarked() {}
|
||||
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
pub fn stable() {}
|
||||
#[stable(feature = "rust1", since = "1.0.0", reason = "text")]
|
||||
|
@ -61,8 +60,6 @@ impl MethodTester {
|
|||
#[unstable(feature = "test_feature", reason = "text")]
|
||||
pub fn method_unstable_text(&self) {}
|
||||
|
||||
pub fn method_unmarked(&self) {}
|
||||
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
pub fn method_stable(&self) {}
|
||||
#[stable(feature = "rust1", since = "1.0.0", reason = "text")]
|
||||
|
@ -79,6 +76,7 @@ impl MethodTester {
|
|||
pub fn method_frozen_text(&self) {}
|
||||
}
|
||||
|
||||
#[stable(feature = "test_feature", since = "1.0.0")]
|
||||
pub trait Trait {
|
||||
#[stable(feature = "test_feature", since = "1.0.0")]
|
||||
#[deprecated(since = "1.0.0")]
|
||||
|
@ -99,8 +97,6 @@ pub trait Trait {
|
|||
#[unstable(feature = "test_feature", reason = "text")]
|
||||
fn trait_unstable_text(&self) {}
|
||||
|
||||
fn trait_unmarked(&self) {}
|
||||
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
fn trait_stable(&self) {}
|
||||
#[stable(feature = "rust1", since = "1.0.0", reason = "text")]
|
||||
|
@ -130,7 +126,6 @@ pub struct DeprecatedStruct { pub i: int }
|
|||
pub struct DeprecatedUnstableStruct { pub i: int }
|
||||
#[unstable(feature = "test_feature")]
|
||||
pub struct UnstableStruct { pub i: int }
|
||||
pub struct UnmarkedStruct { pub i: int }
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
pub struct StableStruct { pub i: int }
|
||||
|
||||
|
@ -142,10 +137,10 @@ pub struct DeprecatedUnitStruct;
|
|||
pub struct DeprecatedUnstableUnitStruct;
|
||||
#[unstable(feature = "test_feature")]
|
||||
pub struct UnstableUnitStruct;
|
||||
pub struct UnmarkedUnitStruct;
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
pub struct StableUnitStruct;
|
||||
|
||||
#[stable(feature = "test_feature", since = "1.0.0")]
|
||||
pub enum Enum {
|
||||
#[stable(feature = "test_feature", since = "1.0.0")]
|
||||
#[deprecated(since = "1.0.0")]
|
||||
|
@ -156,7 +151,6 @@ pub enum Enum {
|
|||
#[unstable(feature = "test_feature")]
|
||||
UnstableVariant,
|
||||
|
||||
UnmarkedVariant,
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
StableVariant,
|
||||
}
|
||||
|
@ -169,7 +163,6 @@ pub struct DeprecatedTupleStruct(pub int);
|
|||
pub struct DeprecatedUnstableTupleStruct(pub int);
|
||||
#[unstable(feature = "test_feature")]
|
||||
pub struct UnstableTupleStruct(pub int);
|
||||
pub struct UnmarkedTupleStruct(pub int);
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
pub struct StableTupleStruct(pub int);
|
||||
|
||||
|
|
|
@ -8,34 +8,38 @@
|
|||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
#![feature(unboxed_closures, overloaded_calls)]
|
||||
#![feature(unboxed_closures)]
|
||||
|
||||
// Tests that we can't assign to or mutably borrow upvars from `Fn`
|
||||
// closures (issue #17780)
|
||||
|
||||
fn set(x: &mut usize) { *x = 5; }
|
||||
|
||||
fn to_fn<A,F:Fn<A>>(f: F) -> F { f }
|
||||
fn to_fn_mut<A,F:FnMut<A>>(f: F) -> F { f }
|
||||
|
||||
fn main() {
|
||||
// By-ref captures
|
||||
{
|
||||
let mut x = 0us;
|
||||
let _f = |&:| x = 42; //~ ERROR cannot assign
|
||||
let _f = to_fn(|| x = 42); //~ ERROR cannot assign
|
||||
|
||||
let mut y = 0us;
|
||||
let _g = |&:| set(&mut y); //~ ERROR cannot borrow
|
||||
let _g = to_fn(|| set(&mut y)); //~ ERROR cannot borrow
|
||||
|
||||
let mut z = 0us;
|
||||
let _h = |&mut:| { set(&mut z); |&:| z = 42; }; //~ ERROR cannot assign
|
||||
let _h = to_fn_mut(|| { set(&mut z); to_fn(|| z = 42); }); //~ ERROR cannot assign
|
||||
}
|
||||
|
||||
// By-value captures
|
||||
{
|
||||
let mut x = 0us;
|
||||
let _f = move |&:| x = 42; //~ ERROR cannot assign
|
||||
let _f = to_fn(move || x = 42); //~ ERROR cannot assign
|
||||
|
||||
let mut y = 0us;
|
||||
let _g = move |&:| set(&mut y); //~ ERROR cannot borrow
|
||||
let _g = to_fn(move || set(&mut y)); //~ ERROR cannot borrow
|
||||
|
||||
let mut z = 0us;
|
||||
let _h = move |&mut:| { set(&mut z); move |&:| z = 42; }; //~ ERROR cannot assign
|
||||
let _h = to_fn_mut(move || { set(&mut z); to_fn(move || z = 42); }); //~ ERROR cannot assign
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,11 +8,14 @@
|
|||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
#![feature(box_syntax)]
|
||||
#![feature(box_syntax,unboxed_closures)]
|
||||
|
||||
fn to_fn_mut<A,F:FnMut<A>>(f: F) -> F { f }
|
||||
fn to_fn_once<A,F:FnOnce<A>>(f: F) -> F { f }
|
||||
|
||||
pub fn main() {
|
||||
let bar = box 3;
|
||||
let _g = |&mut:| {
|
||||
let _h = move |:| -> isize { *bar }; //~ ERROR cannot move out of captured outer variable
|
||||
};
|
||||
let _g = to_fn_mut(|| {
|
||||
let _h = to_fn_once(move || -> isize { *bar }); //~ ERROR cannot move out of
|
||||
});
|
||||
}
|
||||
|
|
|
@ -8,12 +8,16 @@
|
|||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
#![feature(unboxed_closures)]
|
||||
|
||||
fn to_fn_once<A,F:FnOnce<A>>(f: F) -> F { f }
|
||||
|
||||
fn main() {
|
||||
let x = 1;
|
||||
move|:| { x = 2; };
|
||||
to_fn_once(move|:| { x = 2; });
|
||||
//~^ ERROR: cannot assign to immutable captured outer variable
|
||||
|
||||
let s = std::old_io::stdin();
|
||||
move|:| { s.read_to_end(); };
|
||||
to_fn_once(move|:| { s.read_to_end(); });
|
||||
//~^ ERROR: cannot borrow immutable captured outer variable
|
||||
}
|
||||
|
|
|
@ -8,12 +8,14 @@
|
|||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
#![feature(box_syntax)]
|
||||
#![feature(box_syntax, unboxed_closures)]
|
||||
|
||||
fn to_fn_once<A,F:FnOnce<A>>(f: F) -> F { f }
|
||||
|
||||
fn main() {
|
||||
let r = {
|
||||
let x = box 42;
|
||||
let f = move|:| &x; //~ ERROR: `x` does not live long enough
|
||||
let f = to_fn_once(move|| &x); //~ ERROR: `x` does not live long enough
|
||||
f()
|
||||
};
|
||||
|
||||
|
|
|
@ -8,14 +8,15 @@
|
|||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
#![feature(box_syntax)]
|
||||
#![feature(box_syntax, unboxed_closures)]
|
||||
|
||||
fn to_fn_once<A,F:FnOnce<A>>(f: F) -> F { f }
|
||||
fn do_it(x: &isize) { }
|
||||
|
||||
fn main() {
|
||||
let x = box 22;
|
||||
let f = move|:| do_it(&*x);
|
||||
(move|:| {
|
||||
let f = to_fn_once(move|| do_it(&*x));
|
||||
to_fn_once(move|| {
|
||||
f();
|
||||
f();
|
||||
//~^ ERROR: use of moved value: `f`
|
||||
|
|
|
@ -20,7 +20,7 @@
|
|||
#![staged_api]
|
||||
|
||||
#[macro_use]
|
||||
extern crate lint_stability; //~ ERROR: use of unmarked library feature
|
||||
extern crate lint_stability;
|
||||
|
||||
mod cross_crate {
|
||||
extern crate stability_cfg1;
|
||||
|
@ -61,10 +61,6 @@ mod cross_crate {
|
|||
foo.method_unstable_text(); //~ WARNING use of unstable library feature 'test_feature': text
|
||||
foo.trait_unstable_text(); //~ WARNING use of unstable library feature 'test_feature': text
|
||||
|
||||
unmarked(); //~ ERROR use of unmarked library feature
|
||||
foo.method_unmarked(); //~ ERROR use of unmarked library feature
|
||||
foo.trait_unmarked(); //~ ERROR use of unmarked library feature
|
||||
|
||||
stable();
|
||||
foo.method_stable();
|
||||
foo.trait_stable();
|
||||
|
@ -77,28 +73,24 @@ mod cross_crate {
|
|||
let _ = DeprecatedUnstableStruct { i: 0 }; //~ ERROR use of deprecated item
|
||||
//~^ WARNING use of unstable library feature
|
||||
let _ = UnstableStruct { i: 0 }; //~ WARNING use of unstable library feature
|
||||
let _ = UnmarkedStruct { i: 0 }; //~ ERROR use of unmarked library feature
|
||||
let _ = StableStruct { i: 0 };
|
||||
|
||||
let _ = DeprecatedUnitStruct; //~ ERROR use of deprecated item
|
||||
let _ = DeprecatedUnstableUnitStruct; //~ ERROR use of deprecated item
|
||||
//~^ WARNING use of unstable library feature
|
||||
let _ = UnstableUnitStruct; //~ WARNING use of unstable library feature
|
||||
let _ = UnmarkedUnitStruct; //~ ERROR use of unmarked library feature
|
||||
let _ = StableUnitStruct;
|
||||
|
||||
let _ = Enum::DeprecatedVariant; //~ ERROR use of deprecated item
|
||||
let _ = Enum::DeprecatedUnstableVariant; //~ ERROR use of deprecated item
|
||||
//~^ WARNING use of unstable library feature
|
||||
let _ = Enum::UnstableVariant; //~ WARNING use of unstable library feature
|
||||
let _ = Enum::UnmarkedVariant; //~ ERROR use of unmarked library feature
|
||||
let _ = Enum::StableVariant;
|
||||
|
||||
let _ = DeprecatedTupleStruct (1); //~ ERROR use of deprecated item
|
||||
let _ = DeprecatedUnstableTupleStruct (1); //~ ERROR use of deprecated item
|
||||
//~^ WARNING use of unstable library feature
|
||||
let _ = UnstableTupleStruct (1); //~ WARNING use of unstable library feature
|
||||
let _ = UnmarkedTupleStruct (1); //~ ERROR use of unmarked library feature
|
||||
let _ = StableTupleStruct (1);
|
||||
|
||||
// At the moment, the lint checker only checks stability in
|
||||
|
@ -123,7 +115,6 @@ mod cross_crate {
|
|||
//~^ WARNING use of unstable library feature
|
||||
foo.trait_unstable(); //~ WARNING use of unstable library feature
|
||||
foo.trait_unstable_text(); //~ WARNING use of unstable library feature 'test_feature': text
|
||||
foo.trait_unmarked(); //~ ERROR use of unmarked library feature
|
||||
foo.trait_stable();
|
||||
}
|
||||
|
||||
|
@ -136,7 +127,6 @@ mod cross_crate {
|
|||
//~^ WARNING use of unstable library feature
|
||||
foo.trait_unstable(); //~ WARNING use of unstable library feature
|
||||
foo.trait_unstable_text(); //~ WARNING use of unstable library feature 'test_feature': text
|
||||
foo.trait_unmarked(); //~ ERROR use of unmarked library feature
|
||||
foo.trait_stable();
|
||||
}
|
||||
|
||||
|
@ -183,8 +173,6 @@ mod this_crate {
|
|||
#[unstable(feature = "test_feature", reason = "text")]
|
||||
pub fn unstable_text() {}
|
||||
|
||||
pub fn unmarked() {}
|
||||
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
pub fn stable() {}
|
||||
#[stable(feature = "rust1", since = "1.0.0", reason = "text")]
|
||||
|
@ -206,8 +194,6 @@ mod this_crate {
|
|||
#[unstable(feature = "test_feature", reason = "text")]
|
||||
pub fn method_unstable_text(&self) {}
|
||||
|
||||
pub fn method_unmarked(&self) {}
|
||||
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
pub fn method_stable(&self) {}
|
||||
#[stable(feature = "rust1", since = "1.0.0", reason = "text")]
|
||||
|
@ -227,8 +213,6 @@ mod this_crate {
|
|||
#[unstable(feature = "test_feature", reason = "text")]
|
||||
fn trait_unstable_text(&self) {}
|
||||
|
||||
fn trait_unmarked(&self) {}
|
||||
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
fn trait_stable(&self) {}
|
||||
#[stable(feature = "rust1", since = "1.0.0", reason = "text")]
|
||||
|
@ -242,7 +226,6 @@ mod this_crate {
|
|||
pub struct DeprecatedStruct { i: isize }
|
||||
#[unstable(feature = "test_feature")]
|
||||
pub struct UnstableStruct { i: isize }
|
||||
pub struct UnmarkedStruct { i: isize }
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
pub struct StableStruct { i: isize }
|
||||
|
||||
|
@ -251,7 +234,6 @@ mod this_crate {
|
|||
pub struct DeprecatedUnitStruct;
|
||||
#[unstable(feature = "test_feature")]
|
||||
pub struct UnstableUnitStruct;
|
||||
pub struct UnmarkedUnitStruct;
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
pub struct StableUnitStruct;
|
||||
|
||||
|
@ -262,7 +244,6 @@ mod this_crate {
|
|||
#[unstable(feature = "test_feature")]
|
||||
UnstableVariant,
|
||||
|
||||
UnmarkedVariant,
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
StableVariant,
|
||||
}
|
||||
|
@ -272,7 +253,6 @@ mod this_crate {
|
|||
pub struct DeprecatedTupleStruct(isize);
|
||||
#[unstable(feature = "test_feature")]
|
||||
pub struct UnstableTupleStruct(isize);
|
||||
pub struct UnmarkedTupleStruct(isize);
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
pub struct StableTupleStruct(isize);
|
||||
|
||||
|
@ -299,10 +279,6 @@ mod this_crate {
|
|||
foo.method_unstable_text();
|
||||
foo.trait_unstable_text();
|
||||
|
||||
unmarked();
|
||||
foo.method_unmarked();
|
||||
foo.trait_unmarked();
|
||||
|
||||
stable();
|
||||
foo.method_stable();
|
||||
foo.trait_stable();
|
||||
|
@ -313,22 +289,18 @@ mod this_crate {
|
|||
|
||||
let _ = DeprecatedStruct { i: 0 }; //~ ERROR use of deprecated item
|
||||
let _ = UnstableStruct { i: 0 };
|
||||
let _ = UnmarkedStruct { i: 0 };
|
||||
let _ = StableStruct { i: 0 };
|
||||
|
||||
let _ = DeprecatedUnitStruct; //~ ERROR use of deprecated item
|
||||
let _ = UnstableUnitStruct;
|
||||
let _ = UnmarkedUnitStruct;
|
||||
let _ = StableUnitStruct;
|
||||
|
||||
let _ = Enum::DeprecatedVariant; //~ ERROR use of deprecated item
|
||||
let _ = Enum::UnstableVariant;
|
||||
let _ = Enum::UnmarkedVariant;
|
||||
let _ = Enum::StableVariant;
|
||||
|
||||
let _ = DeprecatedTupleStruct (1); //~ ERROR use of deprecated item
|
||||
let _ = UnstableTupleStruct (1);
|
||||
let _ = UnmarkedTupleStruct (1);
|
||||
let _ = StableTupleStruct (1);
|
||||
}
|
||||
|
||||
|
@ -337,7 +309,6 @@ mod this_crate {
|
|||
foo.trait_deprecated_text(); //~ ERROR use of deprecated item: text
|
||||
foo.trait_unstable();
|
||||
foo.trait_unstable_text();
|
||||
foo.trait_unmarked();
|
||||
foo.trait_stable();
|
||||
}
|
||||
|
||||
|
@ -346,7 +317,6 @@ mod this_crate {
|
|||
foo.trait_deprecated_text(); //~ ERROR use of deprecated item: text
|
||||
foo.trait_unstable();
|
||||
foo.trait_unstable_text();
|
||||
foo.trait_unmarked();
|
||||
foo.trait_stable();
|
||||
}
|
||||
|
||||
|
|
33
src/test/compile-fail/missing-stability.rs
Normal file
33
src/test/compile-fail/missing-stability.rs
Normal file
|
@ -0,0 +1,33 @@
|
|||
// Copyright 2015 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.
|
||||
|
||||
// Checks that exported items without stability attributes cause an error
|
||||
|
||||
#![crate_type="lib"]
|
||||
#![feature(staged_api)]
|
||||
#![staged_api]
|
||||
|
||||
pub fn unmarked() {
|
||||
//~^ ERROR This node does not have a stability attribute
|
||||
()
|
||||
}
|
||||
|
||||
#[unstable(feature = "foo")]
|
||||
pub mod foo {
|
||||
// #[unstable] is inherited
|
||||
pub fn unmarked() {}
|
||||
}
|
||||
|
||||
#[stable(feature = "bar", since="1.0.0")]
|
||||
pub mod bar {
|
||||
// #[stable] is not inherited
|
||||
pub fn unmarked() {}
|
||||
//~^ ERROR This node does not have a stability attribute
|
||||
}
|
|
@ -8,13 +8,15 @@
|
|||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
#![feature(box_syntax)]
|
||||
#![feature(box_syntax, unboxed_closures)]
|
||||
|
||||
use std::usize;
|
||||
|
||||
fn to_fn<A,F:Fn<A>>(f: F) -> F { f }
|
||||
|
||||
fn test(_x: Box<usize>) {}
|
||||
|
||||
fn main() {
|
||||
let i = box 3;
|
||||
let _f = |&:| test(i); //~ ERROR cannot move out
|
||||
let _f = to_fn(|| test(i)); //~ ERROR cannot move out
|
||||
}
|
||||
|
|
|
@ -13,4 +13,4 @@
|
|||
pub fn main() {
|
||||
let r = 1..2..3;
|
||||
//~^ ERROR expected one of `.`, `;`, or an operator, found `..`
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,4 +13,4 @@
|
|||
pub fn main() {
|
||||
let r = ..1..2;
|
||||
//~^ ERROR expected one of `.`, `;`, or an operator, found `..`
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,8 +10,10 @@
|
|||
|
||||
#![feature(unboxed_closures)]
|
||||
|
||||
fn to_fn_once<A,F:FnOnce<A>>(f: F) -> F { f }
|
||||
|
||||
fn main() {
|
||||
let f = move|:| ();
|
||||
let f = to_fn_once(move|| ());
|
||||
f();
|
||||
f(); //~ ERROR use of moved value
|
||||
}
|
||||
|
|
|
@ -15,31 +15,35 @@
|
|||
// if the upvar is captured by ref or the closure takes self by
|
||||
// reference.
|
||||
|
||||
fn to_fn<A,F:Fn<A>>(f: F) -> F { f }
|
||||
fn to_fn_mut<A,F:FnMut<A>>(f: F) -> F { f }
|
||||
fn to_fn_once<A,F:FnOnce<A>>(f: F) -> F { f }
|
||||
|
||||
fn main() {
|
||||
// By-ref cases
|
||||
{
|
||||
let x = box 0us;
|
||||
let f = |&:| drop(x); //~ ERROR cannot move
|
||||
let f = to_fn(|| drop(x)); //~ ERROR cannot move
|
||||
}
|
||||
{
|
||||
let x = box 0us;
|
||||
let f = |&mut:| drop(x); //~ ERROR cannot move
|
||||
let f = to_fn_mut(|| drop(x)); //~ ERROR cannot move
|
||||
}
|
||||
{
|
||||
let x = box 0us;
|
||||
let f = |:| drop(x); // OK -- FnOnce
|
||||
let f = to_fn_once(|| drop(x)); // OK -- FnOnce
|
||||
}
|
||||
// By-value cases
|
||||
{
|
||||
let x = box 0us;
|
||||
let f = move |&:| drop(x); //~ ERROR cannot move
|
||||
let f = to_fn(move || drop(x)); //~ ERROR cannot move
|
||||
}
|
||||
{
|
||||
let x = box 0us;
|
||||
let f = move |&mut:| drop(x); //~ ERROR cannot move
|
||||
let f = to_fn_mut(move || drop(x)); //~ ERROR cannot move
|
||||
}
|
||||
{
|
||||
let x = box 0us;
|
||||
let f = move |:| drop(x); // this one is ok
|
||||
let f = to_fn_once(move || drop(x)); // this one is ok
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,51 +12,56 @@
|
|||
// as `mut` through a closure. Also test that we CAN mutate a moved copy,
|
||||
// unless this is a `Fn` closure. Issue #16749.
|
||||
|
||||
#![feature(unboxed_closures)]
|
||||
|
||||
use std::mem;
|
||||
|
||||
fn to_fn<A,F:Fn<A>>(f: F) -> F { f }
|
||||
fn to_fn_mut<A,F:FnMut<A>>(f: F) -> F { f }
|
||||
|
||||
fn a() {
|
||||
let n = 0u8;
|
||||
let mut f = |&mut:| { //~ ERROR closure cannot assign
|
||||
let mut f = to_fn_mut(|| { //~ ERROR closure cannot assign
|
||||
n += 1;
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
fn b() {
|
||||
let mut n = 0u8;
|
||||
let mut f = |&mut:| {
|
||||
let mut f = to_fn_mut(|| {
|
||||
n += 1; // OK
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
fn c() {
|
||||
let n = 0u8;
|
||||
let mut f = move |&mut:| {
|
||||
let mut f = to_fn_mut(move || {
|
||||
// If we just did a straight-forward desugaring, this would
|
||||
// compile, but we do something a bit more subtle, and hence
|
||||
// we get an error.
|
||||
n += 1; //~ ERROR cannot assign
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
fn d() {
|
||||
let mut n = 0u8;
|
||||
let mut f = move |&mut:| {
|
||||
let mut f = to_fn_mut(move || {
|
||||
n += 1; // OK
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
fn e() {
|
||||
let n = 0u8;
|
||||
let mut f = move |&:| {
|
||||
let mut f = to_fn(move || {
|
||||
n += 1; //~ ERROR cannot assign
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
fn f() {
|
||||
let mut n = 0u8;
|
||||
let mut f = move |&:| {
|
||||
let mut f = to_fn(move || {
|
||||
n += 1; //~ ERROR cannot assign
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
fn main() { }
|
||||
|
|
|
@ -10,8 +10,10 @@
|
|||
|
||||
#![feature(unboxed_closures)]
|
||||
|
||||
fn to_fn_mut<A,F:FnMut<A>>(f: F) -> F { f }
|
||||
|
||||
fn main() {
|
||||
let mut_ = |&mut: x| x;
|
||||
let mut_ = to_fn_mut(|x| x);
|
||||
mut_.call((0, )); //~ ERROR does not implement any method in scope named `call`
|
||||
}
|
||||
|
||||
|
|
|
@ -12,12 +12,14 @@
|
|||
|
||||
use std::ops::FnMut;
|
||||
|
||||
fn to_fn_mut<A,F:FnMut<A>>(f: F) -> F { f }
|
||||
|
||||
fn call_it<F:FnMut(isize,isize)->isize>(y: isize, mut f: F) -> isize {
|
||||
f(2, y)
|
||||
}
|
||||
|
||||
pub fn main() {
|
||||
let f = |&mut: x: usize, y: isize| -> isize { (x as isize) + y };
|
||||
let f = to_fn_mut(|x: usize, y: isize| -> isize { (x as isize) + y });
|
||||
let z = call_it(3, f);
|
||||
//~^ ERROR type mismatch
|
||||
//~| ERROR type mismatch
|
||||
|
|
|
@ -1,23 +0,0 @@
|
|||
// Copyright 2014 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.
|
||||
|
||||
#![feature(lang_items, overloaded_calls, unboxed_closures)]
|
||||
|
||||
fn c<F:Fn(isize, isize) -> isize>(f: F) -> isize {
|
||||
f(5, 6)
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let z: isize = 7;
|
||||
assert_eq!(c(|&mut: x: isize, y| x + y + z), 10);
|
||||
//~^ ERROR not implemented
|
||||
//~| ERROR not implemented
|
||||
}
|
||||
|
|
@ -149,4 +149,4 @@ fn main() {
|
|||
assoc_enum(Enum::Variant2(8i64, 9i32));
|
||||
}
|
||||
|
||||
fn zzz() { () }
|
||||
fn zzz() { () }
|
||||
|
|
|
@ -9,11 +9,11 @@
|
|||
// except according to those terms.
|
||||
|
||||
|
||||
fn foo(i: int) -> int { i + 1 }
|
||||
fn foo(i: isize) -> isize { i + 1 }
|
||||
|
||||
fn apply<A, F>(f: F, v: A) -> A where F: FnOnce(A) -> A { f(v) }
|
||||
|
||||
pub fn main() {
|
||||
let f = {|: i| foo(i)};
|
||||
let f = {|i| foo(i)};
|
||||
assert_eq!(apply(f, 2), 3);
|
||||
}
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
use std::slice::SliceExt;
|
||||
use std::old_io::{Command, fs, USER_RWX};
|
||||
use std::os;
|
||||
use std::path::BytesContainer;
|
||||
use std::old_path::BytesContainer;
|
||||
use std::rand::random;
|
||||
|
||||
fn main() {
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
|
||||
// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
|
@ -15,8 +14,8 @@
|
|||
#![feature(box_syntax)]
|
||||
#![feature(unboxed_closures)]
|
||||
|
||||
use std::path::{Path};
|
||||
use std::path;
|
||||
use std::old_path::{Path};
|
||||
use std::old_path;
|
||||
use std::result;
|
||||
use std::thunk::Thunk;
|
||||
|
||||
|
@ -28,7 +27,7 @@ fn tester()
|
|||
result::Result::Ok("more blah".to_string())
|
||||
};
|
||||
|
||||
let path = path::Path::new("blah");
|
||||
let path = old_path::Path::new("blah");
|
||||
assert!(loader(&path).is_ok());
|
||||
}
|
||||
|
||||
|
|
|
@ -14,9 +14,9 @@
|
|||
#![feature(box_syntax)]
|
||||
#![feature(unboxed_closures)]
|
||||
|
||||
struct A { a: Box<int> }
|
||||
struct A { a: Box<isize> }
|
||||
|
||||
fn foo() -> Box<FnMut() -> int + 'static> {
|
||||
fn foo() -> Box<FnMut() -> isize + 'static> {
|
||||
let k = box 22;
|
||||
let _u = A {a: k.clone()};
|
||||
let result = |&mut:| 22;
|
||||
|
|
|
@ -20,7 +20,7 @@ use std::old_io;
|
|||
use std::old_io::fs;
|
||||
use std::old_io::Command;
|
||||
use std::os;
|
||||
use std::path::Path;
|
||||
use std::old_path::Path;
|
||||
|
||||
fn main() {
|
||||
let my_args = os::args();
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
#![feature(unboxed_closures)]
|
||||
|
||||
fn main() {
|
||||
let mut zero = |&mut:| {};
|
||||
let () = zero.call_mut(());
|
||||
let mut zero = || {};
|
||||
let () = zero();
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue