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 #[expect(clippy::seek_from_current, reason = "implements stream_position")]
146 fn stream_position(&mut self) -> Result<u64> {
147 self.seek(SeekFrom::Current(0))
148 }
149
150 /// Seeks relative to the current position.
151 ///
152 /// This is equivalent to `self.seek(SeekFrom::Current(offset))` but
153 /// doesn't return the new position which can allow some implementations
154 /// such as `BufReader` to perform more efficient seeks.
155 ///
156 /// # Example
157 ///
158 /// ```no_run
159 /// use std::{
160 /// io::{self, Seek},
161 /// fs::File,
162 /// };
163 ///
164 /// fn main() -> io::Result<()> {
165 /// let mut f = File::open("foo.txt")?;
166 /// f.seek_relative(10)?;
167 /// assert_eq!(f.stream_position()?, 10);
168 /// Ok(())
169 /// }
170 /// ```
171 #[stable(feature = "seek_seek_relative", since = "1.80.0")]
172 fn seek_relative(&mut self, offset: i64) -> Result<()> {
173 self.seek(SeekFrom::Current(offset))?;
174 Ok(())
175 }
176}
177
178/// The default implementation of [`Seek::stream_len`].
179/// This may be desirable in `libstd` where the default implementation is desirable,
180/// but additional work needs to be done before or after.
181#[doc(hidden)]
182#[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")]
183pub fn stream_len_default<T: Seek + ?Sized>(self_: &mut T) -> Result<u64> {
184 let old_pos = self_.stream_position()?;
185 let len = self_.seek(SeekFrom::End(0))?;
186
187 // Avoid seeking a third time when we were already at the end of the
188 // stream. The branch is usually way cheaper than a seek operation.
189 if old_pos != len {
190 self_.seek(SeekFrom::Start(old_pos))?;
191 }
192
193 Ok(len)
194}
195
196/// Enumeration of possible methods to seek within an I/O object.
197///
198/// It is used by the [`Seek`] trait.
199#[derive(Copy, PartialEq, Eq, Clone, Debug)]
200#[stable(feature = "rust1", since = "1.0.0")]
201#[cfg_attr(not(test), rustc_diagnostic_item = "SeekFrom")]
202pub enum SeekFrom {
203 /// Sets the offset to the provided number of bytes.
204 #[stable(feature = "rust1", since = "1.0.0")]
205 Start(#[stable(feature = "rust1", since = "1.0.0")] u64),
206
207 /// Sets the offset to the size of this object plus the specified number of
208 /// bytes.
209 ///
210 /// It is possible to seek beyond the end of an object, but it's an error to
211 /// seek before byte 0.
212 #[stable(feature = "rust1", since = "1.0.0")]
213 End(#[stable(feature = "rust1", since = "1.0.0")] i64),
214
215 /// Sets the offset to the current position plus the specified number of
216 /// bytes.
217 ///
218 /// It is possible to seek beyond the end of an object, but it's an error to
219 /// seek before byte 0.
220 #[stable(feature = "rust1", since = "1.0.0")]
221 Current(#[stable(feature = "rust1", since = "1.0.0")] i64),
222}