Skip to main content

Frame

Struct Frame 

Source
pub struct Frame(/* private fields */);
Expand description

A frame is a single step of game execution.

Frames are the fundamental unit of time in rollback networking. Each frame represents one discrete step of game simulation. Frame numbers start at 0 and increment sequentially.

The special value NULL_FRAME (-1) represents “no frame” or “uninitialized”.

§Formal Specification Alignment

  • TLA+: Frame == {NULL_FRAME} ∪ (0..MAX_FRAME) in specs/tla/Rollback.tla
  • Z3: Frame arithmetic proofs in tests/test_z3_verification.rs
  • formal-spec.md: Core type definition with operations frame_add, frame_sub, frame_valid
  • Kani: kani_frame_* proofs verify overflow safety and arithmetic correctness

§Type Safety

Frame is a newtype wrapper around i32 that provides:

  • Clear semantic meaning (frames vs arbitrary integers)
  • Helper methods like is_null() and is_valid()
  • Arithmetic operations for frame calculations
  • Compile-time prevention of accidentally mixing frames with other integers

§Examples

use fortress_rollback::{Frame, NULL_FRAME};

// Creating frames
let frame = Frame::new(0);
let null_frame = Frame::NULL;

// Checking validity
assert!(frame.is_valid());
assert!(null_frame.is_null());

// Frame arithmetic
let next_frame = frame + 1;
assert_eq!(next_frame.as_i32(), 1);

// Comparison
assert!(next_frame > frame);

Implementations§

Source§

impl Frame

Source

pub const NULL: Self

The null frame constant, representing “no frame” or “uninitialized”.

This is equivalent to NULL_FRAME (-1).

Source

pub const fn new(frame: i32) -> Self

Creates a new Frame from an i32 value.

Note: This does not validate the frame number. Use Frame::is_valid() to check if the frame represents a valid (non-negative) frame number.

Source

pub const fn as_i32(self) -> i32

Returns the underlying i32 value.

Source

pub const fn is_null(self) -> bool

Returns true if this frame is the null frame (equivalent to NULL_FRAME).

§Examples
use fortress_rollback::Frame;

assert!(Frame::NULL.is_null());
assert!(!Frame::new(0).is_null());
Source

pub const fn is_valid(self) -> bool

Returns true if this frame is valid (non-negative).

§Examples
use fortress_rollback::Frame;

assert!(Frame::new(0).is_valid());
assert!(Frame::new(100).is_valid());
assert!(!Frame::NULL.is_valid());
assert!(!Frame::new(-5).is_valid());
Source

pub const fn to_option(self) -> Option<Self>

Returns Some(self) if the frame is valid, or None if it’s null or negative.

This is useful for handling the null/valid frame pattern with Option.

Source

pub const fn from_option(opt: Option<Self>) -> Self

Creates a Frame from an Option, using NULL for None.

Source

pub const fn checked_add(self, rhs: i32) -> Option<Self>

Adds a value to this frame, returning None if overflow occurs.

This is the preferred method for frame arithmetic when overflow must be handled.

§Examples
use fortress_rollback::Frame;

let frame = Frame::new(100);
assert_eq!(frame.checked_add(50), Some(Frame::new(150)));
assert_eq!(Frame::new(i32::MAX).checked_add(1), None);
Source

pub const fn checked_sub(self, rhs: i32) -> Option<Self>

Subtracts a value from this frame, returning None if overflow occurs.

This is the preferred method for frame arithmetic when overflow must be handled.

§Examples
use fortress_rollback::Frame;

let frame = Frame::new(100);
assert_eq!(frame.checked_sub(50), Some(Frame::new(50)));
assert_eq!(Frame::new(i32::MIN).checked_sub(1), None);
Source

pub const fn saturating_add(self, rhs: i32) -> Self

Adds a value to this frame, saturating at the numeric bounds.

Use this when clamping to bounds is acceptable (e.g., frame counters that should never go negative or exceed maximum).

§Examples
use fortress_rollback::Frame;

let frame = Frame::new(100);
assert_eq!(frame.saturating_add(50), Frame::new(150));
assert_eq!(Frame::new(i32::MAX).saturating_add(1), Frame::new(i32::MAX));
Source

pub const fn saturating_sub(self, rhs: i32) -> Self

Subtracts a value from this frame, saturating at the numeric bounds.

Use this when clamping to bounds is acceptable (e.g., ensuring frame never goes below zero or i32::MIN).

§Examples
use fortress_rollback::Frame;

let frame = Frame::new(100);
assert_eq!(frame.saturating_sub(50), Frame::new(50));
assert_eq!(Frame::new(i32::MIN).saturating_sub(1), Frame::new(i32::MIN));
Source

pub const fn abs_diff(self, other: Self) -> u32

Returns the absolute difference between two frames.

This is useful for calculating frame distances without worrying about the order of operands.

§Examples
use fortress_rollback::Frame;

let a = Frame::new(100);
let b = Frame::new(150);
assert_eq!(a.abs_diff(b), 50);
assert_eq!(b.abs_diff(a), 50);
Source

pub const fn as_usize(self) -> Option<usize>

Returns the frame as a usize, or None if the frame is negative.

This is useful for indexing into arrays or vectors where a valid (non-negative) frame is required.

§Examples
use fortress_rollback::Frame;

assert_eq!(Frame::new(42).as_usize(), Some(42));
assert_eq!(Frame::new(0).as_usize(), Some(0));
assert_eq!(Frame::NULL.as_usize(), None);
assert_eq!(Frame::new(-5).as_usize(), None);
Source

pub fn try_as_usize(self) -> Result<usize, FortressError>

Returns the frame as a usize, or a FortressError if negative.

This is the Result-returning version of as_usize, useful when you want to use the ? operator for error propagation.

§Errors

Returns FortressError::InvalidFrameStructured with reason InvalidFrameReason::MustBeNonNegative if the frame is negative.

§Examples
use fortress_rollback::{Frame, FortressError, InvalidFrameReason};

// Successful conversion
let value = Frame::new(42).try_as_usize()?;
assert_eq!(value, 42);

// Error case - negative frame
let result = Frame::NULL.try_as_usize();
assert!(matches!(
    result,
    Err(FortressError::InvalidFrameStructured {
        frame,
        reason: InvalidFrameReason::MustBeNonNegative,
    }) if frame == Frame::NULL
));
Source

pub const fn buffer_index(self, buffer_size: usize) -> Option<usize>

Calculates the buffer index for this frame using modular arithmetic.

This is a common pattern for ring buffer indexing where you need to map a frame number to a buffer slot. Returns None if the frame is negative or if buffer_size is zero.

§Examples
use fortress_rollback::Frame;

// Frame 7 in a buffer of size 4 -> index 3
assert_eq!(Frame::new(7).buffer_index(4), Some(3));

// Frame 0 in a buffer of size 4 -> index 0
assert_eq!(Frame::new(0).buffer_index(4), Some(0));

// Negative frame returns None
assert_eq!(Frame::NULL.buffer_index(4), None);

// Zero buffer size returns None
assert_eq!(Frame::new(5).buffer_index(0), None);
Source

pub fn try_buffer_index( self, buffer_size: usize, ) -> Result<usize, FortressError>

Calculates the buffer index for this frame, returning an error for invalid frames.

This is the Result-returning version of buffer_index().

§Errors

Returns FortressError::InvalidFrameStructured if the frame is negative. Returns FortressError::InvalidRequestStructured with InvalidRequestKind::ZeroBufferSize if buffer_size is zero.

§Examples
use fortress_rollback::{Frame, FortressError, InvalidRequestKind};

// Valid frame and buffer size
let index = Frame::new(7).try_buffer_index(4)?;
assert_eq!(index, 3);

// Negative frame returns error
assert!(Frame::NULL.try_buffer_index(4).is_err());

// Zero buffer size returns error
let result = Frame::new(5).try_buffer_index(0);
assert!(matches!(
    result,
    Err(FortressError::InvalidRequestStructured {
        kind: InvalidRequestKind::ZeroBufferSize
    })
));
Source

pub fn try_add(self, rhs: i32) -> Result<Self, FortressError>

Adds a value to this frame, returning an error if overflow occurs.

This is the Result-returning version of checked_add, useful when you want to use the ? operator for error propagation.

§Errors

Returns FortressError::FrameArithmeticOverflow if the addition would overflow.

§Examples
use fortress_rollback::{Frame, FortressError};

let frame = Frame::new(100);
let result = frame.try_add(50)?;
assert_eq!(result, Frame::new(150));

// Overflow returns error
let overflow_result = Frame::new(i32::MAX).try_add(1);
assert!(matches!(overflow_result, Err(FortressError::FrameArithmeticOverflow { .. })));
Source

pub fn try_sub(self, rhs: i32) -> Result<Self, FortressError>

Subtracts a value from this frame, returning an error if overflow occurs.

This is the Result-returning version of checked_sub, useful when you want to use the ? operator for error propagation.

§Errors

Returns FortressError::FrameArithmeticOverflow if the subtraction would overflow.

§Examples
use fortress_rollback::{Frame, FortressError};

let frame = Frame::new(100);
let result = frame.try_sub(50)?;
assert_eq!(result, Frame::new(50));

// Overflow returns error
let overflow_result = Frame::new(i32::MIN).try_sub(1);
assert!(matches!(overflow_result, Err(FortressError::FrameArithmeticOverflow { .. })));
Source

pub fn next(self) -> Result<Self, FortressError>

Returns the next frame, or an error if overflow would occur.

This is equivalent to try_add(1).

§Errors

Returns FortressError::FrameArithmeticOverflow if the frame is i32::MAX.

§Examples
use fortress_rollback::{Frame, FortressError};

let next_frame = Frame::new(5).next()?;
assert_eq!(next_frame, Frame::new(6));

// MAX returns error
assert!(Frame::new(i32::MAX).next().is_err());
Source

pub fn prev(self) -> Result<Self, FortressError>

Returns the previous frame, or an error if overflow would occur.

This is equivalent to try_sub(1).

§Errors

Returns FortressError::FrameArithmeticOverflow if the frame is i32::MIN.

§Examples
use fortress_rollback::{Frame, FortressError};

let prev_frame = Frame::new(5).prev()?;
assert_eq!(prev_frame, Frame::new(4));

// MIN returns error
assert!(Frame::new(i32::MIN).prev().is_err());
Source

pub const fn saturating_next(self) -> Self

Returns the next frame, saturating at i32::MAX.

This is equivalent to saturating_add(1).

§Examples
use fortress_rollback::Frame;

assert_eq!(Frame::new(5).saturating_next(), Frame::new(6));
assert_eq!(Frame::new(i32::MAX).saturating_next(), Frame::new(i32::MAX));
Source

pub const fn saturating_prev(self) -> Self

Returns the previous frame, saturating at i32::MIN.

This is equivalent to saturating_sub(1).

§Examples
use fortress_rollback::Frame;

assert_eq!(Frame::new(5).saturating_prev(), Frame::new(4));
assert_eq!(Frame::new(i32::MIN).saturating_prev(), Frame::new(i32::MIN));
Source

pub const fn from_usize(value: usize) -> Option<Self>

Creates a Frame from a usize, returning None if it exceeds i32::MAX.

This is useful for converting array indices or sizes to frames safely.

§Examples
use fortress_rollback::Frame;

assert_eq!(Frame::from_usize(42), Some(Frame::new(42)));
assert_eq!(Frame::from_usize(0), Some(Frame::new(0)));

// Values exceeding i32::MAX return None
let too_large = (i32::MAX as usize) + 1;
assert_eq!(Frame::from_usize(too_large), None);
Source

pub fn try_from_usize(value: usize) -> Result<Self, FortressError>

Creates a Frame from a usize, returning an error if it exceeds i32::MAX.

This is the Result-returning version of from_usize, useful when you want to use the ? operator for error propagation.

§Errors

Returns FortressError::FrameValueTooLarge if the value exceeds i32::MAX.

§Examples
use fortress_rollback::{Frame, FortressError};

let frame = Frame::try_from_usize(42)?;
assert_eq!(frame, Frame::new(42));

// Values exceeding i32::MAX return error
let too_large = (i32::MAX as usize) + 1;
let result = Frame::try_from_usize(too_large);
assert!(matches!(result, Err(FortressError::FrameValueTooLarge { .. })));
Source

pub const fn distance_to(self, other: Self) -> Option<i32>

Returns the signed distance from self to other (other - self).

Returns None if the subtraction would overflow.

§Examples
use fortress_rollback::Frame;

let a = Frame::new(100);
let b = Frame::new(150);

assert_eq!(a.distance_to(b), Some(50));
assert_eq!(b.distance_to(a), Some(-50));
assert_eq!(a.distance_to(a), Some(0));

// Overflow returns None
assert_eq!(Frame::new(i32::MIN).distance_to(Frame::new(i32::MAX)), None);
Source

pub const fn is_within(self, window: u32, reference: Self) -> bool

Returns true if self is within window frames of reference.

This checks if the absolute difference between self and reference is less than or equal to window.

§Examples
use fortress_rollback::Frame;

let reference = Frame::new(100);

// Within window
assert!(Frame::new(98).is_within(5, reference));  // diff = 2
assert!(Frame::new(105).is_within(5, reference)); // diff = 5

// At boundary
assert!(Frame::new(95).is_within(5, reference));  // diff = 5 (exact)

// Outside window
assert!(!Frame::new(94).is_within(5, reference)); // diff = 6
assert!(!Frame::new(106).is_within(5, reference)); // diff = 6

Trait Implementations§

Source§

impl Add<i32> for Frame

Source§

type Output = Frame

The resulting type after applying the + operator.
Source§

fn add(self, rhs: i32) -> Self::Output

Performs the + operation. Read more
Source§

impl Add for Frame

Source§

type Output = Frame

The resulting type after applying the + operator.
Source§

fn add(self, rhs: Self) -> Self::Output

Performs the + operation. Read more
Source§

impl AddAssign<i32> for Frame

Source§

fn add_assign(&mut self, rhs: i32)

Performs the += operation. Read more
Source§

impl Clone for Frame

Source§

fn clone(&self) -> Frame

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for Frame

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Default for Frame

Source§

fn default() -> Frame

Returns the “default value” for a type. Read more
Source§

impl<'de> Deserialize<'de> for Frame

Source§

fn deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>
where __D: Deserializer<'de>,

Deserialize this value from the given Serde deserializer. Read more
Source§

impl Display for Frame

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl From<Frame> for i32

Source§

fn from(frame: Frame) -> Self

Converts to this type from the input type.
Source§

impl From<i32> for Frame

Source§

fn from(value: i32) -> Self

Converts to this type from the input type.
Source§

impl From<usize> for Frame

Converts a usize to a Frame.

§⚠️ Discouraged

Soft-deprecated: This conversion silently truncates values larger than i32::MAX. For safe conversion with overflow detection, use Frame::from_usize() or Frame::try_from_usize() instead.

This impl cannot use #[deprecated] because Rust doesn’t support that attribute on trait impl blocks — no compiler warning will be emitted. Consider using the safer alternatives listed above.

Source§

fn from(value: usize) -> Self

Converts to this type from the input type.
Source§

impl Hash for Frame

Source§

fn hash<__H: Hasher>(&self, state: &mut __H)

Feeds this value into the given Hasher. Read more
1.3.0 · Source§

fn hash_slice<H>(data: &[Self], state: &mut H)
where H: Hasher, Self: Sized,

Feeds a slice of this type into the given Hasher. Read more
Source§

impl Ord for Frame

Source§

fn cmp(&self, other: &Frame) -> Ordering

This method returns an Ordering between self and other. Read more
1.21.0 · Source§

fn max(self, other: Self) -> Self
where Self: Sized,

Compares and returns the maximum of two values. Read more
1.21.0 · Source§

fn min(self, other: Self) -> Self
where Self: Sized,

Compares and returns the minimum of two values. Read more
1.50.0 · Source§

fn clamp(self, min: Self, max: Self) -> Self
where Self: Sized,

Restrict a value to a certain interval. Read more
Source§

impl PartialEq<i32> for Frame

Source§

fn eq(&self, other: &i32) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · Source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, and should not be overridden without very good reason.
Source§

impl PartialEq for Frame

Source§

fn eq(&self, other: &Frame) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · Source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, and should not be overridden without very good reason.
Source§

impl PartialOrd<i32> for Frame

Source§

fn partial_cmp(&self, other: &i32) -> Option<Ordering>

This method returns an ordering between self and other values if one exists. Read more
1.0.0 · Source§

fn lt(&self, other: &Rhs) -> bool

Tests less than (for self and other) and is used by the < operator. Read more
1.0.0 · Source§

fn le(&self, other: &Rhs) -> bool

Tests less than or equal to (for self and other) and is used by the <= operator. Read more
1.0.0 · Source§

fn gt(&self, other: &Rhs) -> bool

Tests greater than (for self and other) and is used by the > operator. Read more
1.0.0 · Source§

fn ge(&self, other: &Rhs) -> bool

Tests greater than or equal to (for self and other) and is used by the >= operator. Read more
Source§

impl PartialOrd for Frame

Source§

fn partial_cmp(&self, other: &Frame) -> Option<Ordering>

This method returns an ordering between self and other values if one exists. Read more
1.0.0 · Source§

fn lt(&self, other: &Rhs) -> bool

Tests less than (for self and other) and is used by the < operator. Read more
1.0.0 · Source§

fn le(&self, other: &Rhs) -> bool

Tests less than or equal to (for self and other) and is used by the <= operator. Read more
1.0.0 · Source§

fn gt(&self, other: &Rhs) -> bool

Tests greater than (for self and other) and is used by the > operator. Read more
1.0.0 · Source§

fn ge(&self, other: &Rhs) -> bool

Tests greater than or equal to (for self and other) and is used by the >= operator. Read more
Source§

impl Rem<i32> for Frame

Source§

type Output = i32

The resulting type after applying the % operator.
Source§

fn rem(self, rhs: i32) -> Self::Output

Performs the % operation. Read more
Source§

impl Serialize for Frame

Source§

fn serialize<__S>(&self, __serializer: __S) -> Result<__S::Ok, __S::Error>
where __S: Serializer,

Serialize this value into the given Serde serializer. Read more
Source§

impl Sub<i32> for Frame

Source§

type Output = Frame

The resulting type after applying the - operator.
Source§

fn sub(self, rhs: i32) -> Self::Output

Performs the - operation. Read more
Source§

impl Sub for Frame

Source§

type Output = i32

The resulting type after applying the - operator.
Source§

fn sub(self, rhs: Self) -> Self::Output

Performs the - operation. Read more
Source§

impl SubAssign<i32> for Frame

Source§

fn sub_assign(&mut self, rhs: i32)

Performs the -= operation. Read more
Source§

impl Copy for Frame

Source§

impl Eq for Frame

Source§

impl StructuralPartialEq for Frame

Auto Trait Implementations§

§

impl Freeze for Frame

§

impl RefUnwindSafe for Frame

§

impl Send for Frame

§

impl Sync for Frame

§

impl Unpin for Frame

§

impl UnwindSafe for Frame

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T> Instrument for T

Source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an Instrumented wrapper. Read more
Source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T> ToString for T
where T: Display + ?Sized,

Source§

fn to_string(&self) -> String

Converts the given value to a String. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
Source§

impl<T> WithSubscriber for T

Source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a WithDispatch wrapper. Read more
Source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a WithDispatch wrapper. Read more
Source§

impl<T> DeserializeOwned for T
where T: for<'de> Deserialize<'de>,