Skip to main content

core/io/
seek.rs

1use crate::io::Result;
2
3/// The `Seek` trait provides a cursor which can be moved within a stream of
4/// bytes.
5///
6/// The stream typically has a fixed size, allowing seeking relative to either
7/// end or the current offset.
8///
9/// # Examples
10///
11/// `File`s implement `Seek`:
12///
13/// ```no_run
14/// use std::io;
15/// use std::io::prelude::*;
16/// use std::fs::File;
17/// use std::io::SeekFrom;
18///
19/// fn main() -> io::Result<()> {
20///     let mut f = File::open("foo.txt")?;
21///
22///     // move the cursor 42 bytes from the start of the file
23///     f.seek(SeekFrom::Start(42))?;
24///     Ok(())
25/// }
26/// ```
27#[stable(feature = "rust1", since = "1.0.0")]
28#[cfg_attr(not(test), rustc_diagnostic_item = "IoSeek")]
29pub trait Seek {
30    /// Seek to an offset, in bytes, in a stream.
31    ///
32    /// A seek beyond the end of a stream is allowed, but behavior is defined
33    /// by the implementation.
34    ///
35    /// If the seek operation completed successfully,
36    /// this method returns the new position from the start of the stream.
37    /// That position can be used later with [`SeekFrom::Start`].
38    ///
39    /// # Errors
40    ///
41    /// Seeking can fail, for example because it might involve flushing a buffer.
42    ///
43    /// Seeking to a negative offset is considered an error.
44    #[stable(feature = "rust1", since = "1.0.0")]
45    fn seek(&mut self, pos: SeekFrom) -> Result<u64>;
46
47    /// Rewind to the beginning of a stream.
48    ///
49    /// This is a convenience method, equivalent to `seek(SeekFrom::Start(0))`.
50    ///
51    /// # Errors
52    ///
53    /// Rewinding can fail, for example because it might involve flushing a buffer.
54    ///
55    /// # Example
56    ///
57    /// ```no_run
58    /// use std::io::{Read, Seek, Write};
59    /// use std::fs::OpenOptions;
60    ///
61    /// let mut f = OpenOptions::new()
62    ///     .write(true)
63    ///     .read(true)
64    ///     .create(true)
65    ///     .open("foo.txt")?;
66    ///
67    /// let hello = "Hello!\n";
68    /// write!(f, "{hello}")?;
69    /// f.rewind()?;
70    ///
71    /// let mut buf = String::new();
72    /// f.read_to_string(&mut buf)?;
73    /// assert_eq!(&buf, hello);
74    /// # std::io::Result::Ok(())
75    /// ```
76    #[stable(feature = "seek_rewind", since = "1.55.0")]
77    fn rewind(&mut self) -> Result<()> {
78        self.seek(SeekFrom::Start(0))?;
79        Ok(())
80    }
81
82    /// Returns the length of this stream (in bytes).
83    ///
84    /// The default implementation uses up to three seek operations. If this
85    /// method returns successfully, the seek position is unchanged (i.e. the
86    /// position before calling this method is the same as afterwards).
87    /// However, if this method returns an error, the seek position is
88    /// unspecified.
89    ///
90    /// If you need to obtain the length of *many* streams and you don't care
91    /// about the seek position afterwards, you can reduce the number of seek
92    /// operations by simply calling `seek(SeekFrom::End(0))` and using its
93    /// return value (it is also the stream length).
94    ///
95    /// Note that length of a stream can change over time (for example, when
96    /// data is appended to a file). So calling this method multiple times does
97    /// not necessarily return the same length each time.
98    ///
99    /// # Example
100    ///
101    /// ```no_run
102    /// #![feature(seek_stream_len)]
103    /// use std::{
104    ///     io::{self, Seek},
105    ///     fs::File,
106    /// };
107    ///
108    /// fn main() -> io::Result<()> {
109    ///     let mut f = File::open("foo.txt")?;
110    ///
111    ///     let len = f.stream_len()?;
112    ///     println!("The file is currently {len} bytes long");
113    ///     Ok(())
114    /// }
115    /// ```
116    #[unstable(feature = "seek_stream_len", issue = "59359")]
117    fn stream_len(&mut self) -> Result<u64> {
118        stream_len_default(self)
119    }
120
121    /// Returns the current seek position from the start of the stream.
122    ///
123    /// This is equivalent to `self.seek(SeekFrom::Current(0))`.
124    ///
125    /// # Example
126    ///
127    /// ```no_run
128    /// use std::{
129    ///     io::{self, BufRead, BufReader, Seek},
130    ///     fs::File,
131    /// };
132    ///
133    /// fn main() -> io::Result<()> {
134    ///     let mut f = BufReader::new(File::open("foo.txt")?);
135    ///
136    ///     let before = f.stream_position()?;
137    ///     f.read_line(&mut String::new())?;
138    ///     let after = f.stream_position()?;
139    ///
140    ///     println!("The first line was {} bytes long", after - before);
141    ///     Ok(())
142    /// }
143    /// ```
144    #[stable(feature = "seek_convenience", since = "1.51.0")]
145    fn stream_position(&mut self) -> Result<u64> {
146        self.seek(SeekFrom::Current(0))
147    }
148
149    /// Seeks relative to the current position.
150    ///
151    /// This is equivalent to `self.seek(SeekFrom::Current(offset))` but
152    /// doesn't return the new position which can allow some implementations
153    /// such as `BufReader` to perform more efficient seeks.
154    ///
155    /// # Example
156    ///
157    /// ```no_run
158    /// use std::{
159    ///     io::{self, Seek},
160    ///     fs::File,
161    /// };
162    ///
163    /// fn main() -> io::Result<()> {
164    ///     let mut f = File::open("foo.txt")?;
165    ///     f.seek_relative(10)?;
166    ///     assert_eq!(f.stream_position()?, 10);
167    ///     Ok(())
168    /// }
169    /// ```
170    #[stable(feature = "seek_seek_relative", since = "1.80.0")]
171    fn seek_relative(&mut self, offset: i64) -> Result<()> {
172        self.seek(SeekFrom::Current(offset))?;
173        Ok(())
174    }
175}
176
177/// The default implementation of [`Seek::stream_len`].
178/// This may be desirable in `libstd` where the default implementation is desirable,
179/// but additional work needs to be done before or after.
180#[doc(hidden)]
181#[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")]
182pub fn stream_len_default<T: Seek + ?Sized>(self_: &mut T) -> Result<u64> {
183    let old_pos = self_.stream_position()?;
184    let len = self_.seek(SeekFrom::End(0))?;
185
186    // Avoid seeking a third time when we were already at the end of the
187    // stream. The branch is usually way cheaper than a seek operation.
188    if old_pos != len {
189        self_.seek(SeekFrom::Start(old_pos))?;
190    }
191
192    Ok(len)
193}
194
195/// Enumeration of possible methods to seek within an I/O object.
196///
197/// It is used by the [`Seek`] trait.
198#[derive(Copy, PartialEq, Eq, Clone, Debug)]
199#[stable(feature = "rust1", since = "1.0.0")]
200#[cfg_attr(not(test), rustc_diagnostic_item = "SeekFrom")]
201pub enum SeekFrom {
202    /// Sets the offset to the provided number of bytes.
203    #[stable(feature = "rust1", since = "1.0.0")]
204    Start(#[stable(feature = "rust1", since = "1.0.0")] u64),
205
206    /// Sets the offset to the size of this object plus the specified number of
207    /// bytes.
208    ///
209    /// It is possible to seek beyond the end of an object, but it's an error to
210    /// seek before byte 0.
211    #[stable(feature = "rust1", since = "1.0.0")]
212    End(#[stable(feature = "rust1", since = "1.0.0")] i64),
213
214    /// Sets the offset to the current position plus the specified number of
215    /// bytes.
216    ///
217    /// It is possible to seek beyond the end of an object, but it's an error to
218    /// seek before byte 0.
219    #[stable(feature = "rust1", since = "1.0.0")]
220    Current(#[stable(feature = "rust1", since = "1.0.0")] i64),
221}