tag preguntas las kpop canciones rust lifetime

rust - kpop - tag de las 20 preguntas



¿Cómo especifico los parámetros de vida en un tipo asociado? (2)

En el futuro, want un constructor de tipo asociado para su vida útil ''a pero Rust no lo admite todavía. Ver RFC 1598

Tengo este rasgo y estructura simple:

use std::path::{Path, PathBuf}; trait Foo { type Item: AsRef<Path>; type Iter: Iterator<Item = Self::Item>; fn get(&self) -> Self::Iter; } struct Bar { v: Vec<PathBuf>, }

Me gustaría implementar el rasgo de Foo para Bar :

impl Foo for Bar { type Item = PathBuf; type Iter = std::slice::Iter<PathBuf>; fn get(&self) -> Self::Iter { self.v.iter() } }

Sin embargo estoy recibiendo este error:

error[E0106]: missing lifetime specifier --> src/main.rs:16:17 | 16 | type Iter = std::slice::Iter<PathBuf>; | ^^^^^^^^^^^^^^^^^^^^^^^^^ expected lifetime parameter

No encontré manera de especificar tiempos de vida dentro de ese tipo asociado. En particular, quiero expresar que el iterador no puede sobrevivir a la self vida.

¿Cómo debo modificar el rasgo de Foo o la implementación del rasgo de Bar para que esto funcione?

Patio de roya


Hay dos soluciones para su problema. Comencemos con el más simple:

Añade una vida a tu rasgo

trait Foo<''a> { type Item: AsRef<Path>; type Iter: Iterator<Item = Self::Item>; fn get(&''a self) -> Self::Iter; }

Esto requiere que anote la vida útil en todos los lugares donde utilice el rasgo. Cuando implementas el rasgo, necesitas hacer una implementación genérica:

impl<''a> Foo<''a> for Bar { type Item = &''a PathBuf; type Iter = std::slice::Iter<''a, PathBuf>; fn get(&''a self) -> Self::Iter { self.v.iter() } }

Cuando necesita el rasgo para un argumento genérico, también debe asegurarse de que todas las referencias a su objeto de rasgo tengan la misma duración:

fn fooget<''a, T: Foo<''a>>(foo: &''a T) {}

Implementar el rasgo para una referencia a su tipo.

En lugar de implementar el rasgo para su tipo, implementarlo para una referencia a su tipo. El rasgo nunca necesita saber nada sobre vidas de esta manera.

La función de rasgo entonces debe tomar su argumento por valor. En su caso, implementará el rasgo para una referencia:

trait Foo { type Item: AsRef<Path>; type Iter: Iterator<Item = Self::Item>; fn get(self) -> Self::Iter; } impl<''a> Foo for &''a Bar { type Item = &''a PathBuf; type Iter = std::slice::Iter<''a, PathBuf>; fn get(self) -> Self::Iter { self.v.iter() } }

Tu función fooget ahora simplemente se convierte en

fn fooget<T: Foo>(foo: T) {}

El problema con esto es que la función fooget no sabe que T es en realidad un &Bar . Cuando llamas a la función de get , en realidad estás saliendo de la variable foo . No te mueves fuera del objeto, solo mueves la referencia. Si su función fooget intenta llamar a get dos veces, la función no se compilará.

Si desea que su función fooget solo acepte argumentos donde se implementa el rasgo Foo para referencias, debe establecer explícitamente este límite:

fn fooget_twice<''a, T>(foo: &''a T) where &''a T: Foo, {}

La cláusula where se asegura de que solo se llame a esta función para obtener referencias donde se implementó Foo para la referencia en lugar del tipo. También se puede implementar para ambos.

Técnicamente, el compilador podría inferir automáticamente la vida útil en fooget_twice para que puedas escribirlo como

n fooget_twice<T>(foo: &T) where &T: Foo, {}

pero todavía no es lo suficientemente inteligente.

Para casos más complicados, puede usar una función de Rust que aún no está implementada: tipos asociados genéricos (GAT). El trabajo para eso está siendo rastreado en el número 44265 .