Note that I'm still drafting this out, so it's not a "set-in-stone" RFC.
Feature Name: Range Extension Syntax and Offset Method
- Feature Name:
range_offset
- Start Date: __
- RFC PR: __
- Rust Issue: __
Summary
Introduce new syntax for ranges to allow for a more concise representation of ranges with a specified length or offset from the start. Additionally, introduce an offset
method for ranges to achieve similar functionality programmatically.
Motivation
Rust's range syntax offers robust capabilities, but developers frequently find themselves defining a range by specifying a starting point and then determining the end based on a certain offset or length from that start. This is especially prevalent when dealing with byte arrays or general buffers. In such cases, the need arises to define a range that spans from an initial index to another index derived from an offset. This proposal seeks to streamline these common patterns, enhancing clarity and efficiency in both code and functionality.
Guide-level explanation
With this proposal, the following new range syntaxes and methods are introduced:
Proposal:
-
start..+offset
: Represents a range starting atstart
and extending byoffset
units. This syntax introduces a concise way to define a range that begins at a specified start value and extends forward by a given offset. The resulting range is exclusive of the end value. -
start..+=offset
: Represents a range starting atstart
and extending byoffset
units, inclusive of the end. This syntax is similar to the previous one but includes the end value in the range, making it an inclusive range. It starts at the specified start value and extends forward by the given offset, including the end value. Note that using+=
syntax felt more intuitive and understandable than=+
, but one could still argue for the other -
offset(self, length: usize) -> Range
: A method on theRangeFrom
type that extends the range by the specified length. The .offset method is proposed as an extension to theRangeFrom
type in Rust's standard library. The primary goal of this method is to provide a concise and intuitive way to define a range that starts from a specified value and extends by a given offset. Note that this is not exclusive toRangeFrom
, so it could be defined for other range members too.
Examples:
let v_len = 10;
// Using the new syntax:
let range1 = v_len..+5; // Equivalent to [v_len, v_len+5) interval, or v_len..v_len+5 range
let range2 = v_len..+=5; // Equivalent to [v_len, v_len+5] interval, or v_len..=v_len+5 range
// Using the offset method:
let range4 = (v_len..).offset(5); // Equivalent to v_len..v_len+5
Basic Implementation Strategy
There are two primary ways to implement the proposed .offset
method:
1. Extending the Existing RangeFrom
Types
Given a RangeFrom instance, the .offset method would take a single argument, the offset, and return a Range. This is a straightforward approach and would not introduce new types into the standard library.
impl<T: Add<Output = T> + Copy> RangeFrom<T> {
pub fn offset(self, length: T) -> Range<T> {
Range {
start: self.start,
end: self.start + length,
}
}
}
Pros:
- No new types introduced, keeping the standard library lean.
- Direct and intuitive for users familiar with the existing
Range
types.
2. Introducing a New RangeOffset
Struct
This approach involves creating a new struct, RangeOffset
, which represents a range with a specified offset. The .offset
method would then return this new type.
Pros:
- Clear separation between traditional ranges and offset-based ranges.
- Allows for potential additional methods or properties specific to offset-based ranges in the future.
Cons:
- Introduces a new type, which can increase the complexity of the standard library.
- Users would need to become familiar with another range type.
Decision Factors:
-
Backward Compatibility: Modifying the existing
Range
types might have implications for backward compatibility. Introducing a new type would sidestep this issue. -
Intuitiveness: Using the existing
Range
types might be more intuitive for users, as they wouldn't need to learn about a new type. However, a new type could provide clearer semantics for offset-based operations. -
Flexibility: A new type offers more flexibility for future extensions specific to offset-based ranges.
Given these considerations, community feedback would be appreciated. Both strategies have their merits, and the decision should weigh the benefits of simplicity and intuitiveness against the flexibility and clarity of introducing a new type.
Reference-level explanation
The compiler's handling of range syntax would need to be extended to recognize and correctly parse the new patterns. The resulting Range objects would be equivalent to those produced by the existing syntax. Additionally, the Range type would need to be extended to include the offset method.
Drawbacks
Introduces new syntax and methods which might have a learning curve. Potential for confusion with multiple ways to express similar ranges.
Rationale and alternatives
This proposal makes certain range patterns more concise both syntactically and programmatically. An alternative is to rely solely on methods like offset without introducing new syntax. Another alternative is to not make any changes and rely on the existing syntax and methods.
Prior art
Languages like Python have slicing syntax, but Rust's range syntax is unique. This proposal is about making Rust's range syntax and methods more expressive for common patterns.
Unresolved questions
- Are there potential parsing ambiguities introduced by this syntax? e.g
start.. + 5
withAdd<usize>
? (Through, orphan rule prevents this) - How would this new syntax and method interact with other potential future changes to Rust's range syntax or type system?
- Is this already solved by another library crate?