Home > Enterprise >  Does mixing impl with generic parameters and without them have any implications?
Does mixing impl with generic parameters and without them have any implications?

Time:05-23

Say, I have the following code:

use std::ptr::NonNull;
use std::marker::PhantomPinned;
use std::pin::Pin;

#[derive(Debug)]
struct Unmovable {
    data: Vec<String>,
    cache: Option<NonNull<String>>,
    _pin: PhantomPinned,
}

impl<'a> Unmovable {
    pub fn new(data: Vec<String>) -> Pin<Box<Self>> {
        let internal = Self {
            data,
            cache: None,
            _pin: PhantomPinned
        };

        let mut boxed = Box::pin(internal);
        let cache = if boxed.data.len() > 0 {
            Some(NonNull::from(&boxed.data[0]))
        } else {
            None
        };

        unsafe {
            let ref_mut: Pin<&mut Unmovable> = Pin::as_mut(&mut boxed);
            Pin::get_unchecked_mut(ref_mut).cache = cache;
        }

        boxed
    }
    pub fn get_cache(&'a self) -> Option<&'a String> {
        Some(unsafe { self.cache?.as_ref() })
    }
}

fn main() {
    let unm = Unmovable::new(vec!["hello".to_owned(), "there".to_owned()]);
    println!("{:?}", unm);
    println!("{:?}", unm.get_cache());
}

I wonder, is there any difference at all whether the function new is implemented within a generic-specified impl block or separately without that generic parameter, given that it is not used there. To tell the truth, I'm somewhat surprised that Rust does allow this, impl a function which does not make use of all generics, since it is forbidden for structs, for instance. So I'm quite curious if there are some implications about such a mixed declaration.

Would the new function be tantamount to a separate implementation without generics next to impl?

CodePudding user response:

It will be disallowed for type parameters or const generics. But free lifetimes on impl are allowed. They are only disallowed if used in an associated type. The rationale is explained in the source code:

    // (*) This is a horrible concession to reality. I think it'd be
    // better to just ban unconstrained lifetimes outright, but in
    // practice people do non-hygienic macros like:
    //
    // ```
    // macro_rules! __impl_slice_eq1 {
    //     ($Lhs: ty, $Rhs: ty, $Bound: ident) => {
    //         impl<'a, 'b, A: $Bound, B> PartialEq<$Rhs> for $Lhs where A: PartialEq<B> {
    //            ....
    //         }
    //     }
    // }
    // ```
    //
    // In a concession to backwards compatibility, we continue to
    // permit those, so long as the lifetimes aren't used in
    // associated types. I believe this is sound, because lifetimes
    // used elsewhere are not projected back out.

I don't know enough about rustc's internals to say for sure, but I'm pretty sure it will not affect anything. It is best to just not use that; use a lifetime on the methods that need it, not on the impl.

  • Related