Why does compiler want a 'static here? what could generate such an error when struct is cloned

here is some code extract:

impl<K: Kinematic + std::fmt::Debug + Clone> Move<K> for LinearMove {
    fn get_trajectory(
        &self,
        kin: &K,
        start_positions: &HashMap<Axis, f32>,
    ) -> Result<Option<Box<dyn QTrajectory>>> {
        let origin = kin.joints_to_pose(&[
            start_positions[&Axis(1)],
            start_positions[&Axis(2)],
            start_positions[&Axis(3)],
            start_positions[&Axis(4)],
        ]);
        let new_kin = (*kin).clone();
        let traj = Box::new(LinearTrajectory::new(
            new_kin,
            origin,
            self.target,
            self.lin_vel,
            self.lin_acc,
            self.lin_jerk,
            self.rot_vel,
            self.rot_acc,
            self.rot_jerk,
            self.rot_dir,
        )?);

        Ok(Some(traj))
    }
}

where new is defined as

impl<K: Kinematic + std::fmt::Debug + Clone> LinearTrajectory<K> {
    #[allow(clippy::too_many_arguments)]
    pub fn new(
        kinematic: K,
        mut origin: IsometryMatrix3<f32>,
        mut target: IsometryMatrix3<f32>,
        .....

It fails to compile with the following error

error[E0310]: the parameter type `K` may not live long enough
   --> src/motion_service.rs:168:17
    |
168 |         Ok(Some(traj))
    |                 ^^^^
    |                 |
    |                 the parameter type `K` must be valid for the static lifetime...
    |                 ...so that the type `K` will meet its required lifetime bounds
    |
help: consider adding an explicit lifetime bound
    |
142 | impl<K: Kinematic + std::fmt::Debug + Clone + 'static> Move<K> for LinearMove {
    |                                             +++++++++

For more information about this error, try `rustc --explain E0310`.

How is that possible? I cloned the object of type K and passed it a new object that takes ownership of it. What could generate such an error?

In the function signature, Box<dyn QTrajectory> is short for Box<dyn QTrajectory + 'static>, and that in turn means that the base type you are coercing to dyn QTrajectory must satisfy a 'static bound. Here's more about dyn Trait lifetimes.

As an alternative to 'static, if you control the trait, you could probably instead return Result<Option<Box<dyn QTrajectory + 'something>>> where 'something is a generic lifetime on the method along with a K: 'something bound, and perhaps more bounds, depending on which types may possibly be erased into the return type.

Outlive bounds like K: 'static aren't about when values/objects go out of scope or otherwise get destructed. They are constraints on types which indicate the type cannot contain any borrows unless they are valid for at least the lifetime in the bound.

Rust lifetimes (those '_ things) don't exist at runtime at all.

2 Likes

I guess I'll add some more exploration about "ownership".

Some learning material and other articles draw a distinction between owned and borrowed things by saying something like, if you don't meet a 'static bound you must be borrowing, and if you do, you must be owning. Perhaps they give an example like a String versus a &'s str.

The example makes intuitive sense, but the definition of "owning" being tied to a 'static bound falls apart once you consider less trivial types. Let's consider a Vec<T>. Does the Vec<T> own the Ts? I would say yes: when the Vec<T> gets destructed, it also destructs any Ts it is still holding on to, in addition to deallocating the memory it allocated.

But a Vec<&'s str> doesn't satisfy a 'static bound. Does it still own the &'s str? I would say yes. If the fact that the &'s str are freely copy-able shared references makes you hesitate, consider that the same question could be asked of a Vec<MutexGuard<'s, String>>. In this case the contents of the Vec are also not 'static, they are not copy-able, and they definitely have a semantic impact when dropped.

Taking a step backwards, if you have a variable that is itself a MutexGuard<'s, String>, is it owning? The variable doesn't own a String, but it does own the MutexGuard<'s, String>. So perhaps another mental model would be, "it's possible to own borrowing things".

It's definitely possible to clone borrowing things. Every &T implements Clone (and Copy) for example. When you have a generic like K: Clone, any clones will still be of type K -- including any lifetimes.


There are other, more niche cases that come up where the lifetime in a type doesn't even indicate a borrow per se, which I won't dive into here.[1] But hopefully what I've wrote clears up some misconceptions around clones, "owning", and lifetimes.


  1. Passing example: a lifetime in a function pointer. ↩ī¸Ž

3 Likes

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.