LeetCode: FloodFill Rust
Formatting
I assume the indentation to be a copy-paste error when removing the
boilerplate struct Solution mandated by LeetCode.
Run cargo fmt on your code to conform to the standard Rust
formatting guidelines.
Interface
LeetCode made a poor choice by using Vec&lt;Vec&lt;i32&gt;&gt; to represent a
two-dimensional array, which is generally inefficient as it stores
elements in multiple segmented allocations, incur double indirection
costs, and fail to maintain proper structure (against jagged arrays).
We can't do anything against this, though, so I'll ignore it for the
rest of the post.
Similarly, i32 is not ideal for color representation — a
dedicated type (e.g., struct Color(i32);) is clearly superior.  At
the very least, we can somehow improve readability by using a type
alias and use it consistently for colors in the code:
type Color = i32;

i32 vs. usize
Generally, it is preferred to store and compute indexes in usize,
which is the natural type for this purpose.  One way you can avoid
subtracting 1 is by using usize::MAX and wrapping_add.
To convert from the i32 values that the function receives,
try_from is preferred over as, since the latter silently
truncates invalid values.  unwrap can be used here since it's for
LeetCode; in real-world code, the error should be handled accordingly.

Is if (0..image.len()).contains(x1) better than if x1&gt;=0 &amp;&amp; x1&lt;image.len() for the range check?

I'd say yes.  It indicates the intent more clearly.
Simplifying control flow
Instead of wrapping everything in an if expression, check for
image[sr][sc] == new_color and return early.
while let
This:
while !cells.is_empty() {
    if let Some((sr, sc)) = cells.pop_front() {

is just a convoluted way of saying
while let Some((sr, sc)) = cells.pop_front() {


Here's how I modified your implementation:
type Color = i32;

pub fn flood_fill(
    mut image: Vec&lt;Vec&lt;Color&gt;&gt;,
    sr: i32,
    sc: i32,
    new_color: Color,
) -&gt; Vec&lt;Vec&lt;Color&gt;&gt; {
    use std::collections::VecDeque;
    use std::convert::TryFrom;

    let sr = usize::try_from(sr).unwrap();
    let sc = usize::try_from(sc).unwrap();

    let initial_color = image[sr][sc];

    if initial_color == new_color {
        return image;
    }

    let height = image.len();
    let width = image[0].len();

    let mut cells: VecDeque&lt;(usize, usize)&gt; = VecDeque::new();
    cells.push_back((sr, sc));

    while let Some((sr, sc)) = cells.pop_front() {
        let cell = &amp;mut image[sr][sc];

        if *cell != initial_color {
            continue;
        }

        *cell = new_color;

        const OFFSETS: &amp;[(usize, usize)] = &amp;[(0, usize::MAX), (usize::MAX, 0), (0, 1), (1, 0)];

        for (delta_r, delta_c) in OFFSETS.iter().copied() {
            let new_r = sr.wrapping_add(delta_r);
            let new_c = sc.wrapping_add(delta_c);

            if new_r &lt; height &amp;&amp; new_c &lt; width {
                cells.push_back((new_r, new_c));
            }
        }
    }

    image
}