Related
I was playing with the code from this answer but the FromIterator impl does not compile any more:
error[E0276]: impl has stricter requirements than trait --> src/lib.rs:184:9
| 184 | fn from_iter<I: IntoIterator<Item = T> + 'a>(itrbl: I) -> LazyList<'a, T> {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ impl has extra requirement `I: 'a`
For more information about this error, try `rustc --explain E0276`.
The slightly updated code is on the playground.
// only necessary because Box<FnOnce() -> R> doesn't work...
mod thunk {
pub trait Invoke<R = ()> {
fn invoke(self: Box<Self>) -> R;
}
impl<R, F: FnOnce() -> R> Invoke<R> for F {
#[inline(always)]
fn invoke(self: Box<F>) -> R { (*self)() }
}
}
// Lazy is lazily evaluated contained value using the above Invoke trait
// instead of the desire Box<FnOnce() -> T> or a stable FnBox (currently not)...
pub mod lazy {
use crate::thunk::Invoke;
use std::cell::UnsafeCell;
use std::mem::replace;
use std::ops::Deref;
// Lazy is lazily evaluated contained value using the above Invoke trait
// instead of the desire Box<FnOnce() -> T> or a stable FnBox (currently not)...
pub struct Lazy<'a, T: 'a>(UnsafeCell<LazyState<'a, T>>);
enum LazyState<'a, T: 'a> {
Unevaluated(Box<dyn Invoke<T> + 'a>),
EvaluationInProgress,
Evaluated(T),
}
use self::LazyState::*;
impl<'a, T: 'a> Lazy<'a, T> {
#[inline]
pub fn new<F: 'a + FnOnce() -> T>(func: F) -> Lazy<'a, T> {
Lazy(UnsafeCell::new(Unevaluated(Box::new(func))))
}
#[inline]
pub fn evaluated(val: T) -> Lazy<'a, T> {
Lazy(UnsafeCell::new(Evaluated(val)))
}
#[inline(always)]
fn force(&self) {
unsafe {
match *self.0.get() {
Evaluated(_) => {}, // nothing required; already Evaluated
EvaluationInProgress => panic!("Lazy::force called recursively!!!"),
_ => {
let ue = replace(&mut *self.0.get(), EvaluationInProgress);
if let Unevaluated(thnk) = ue {
*self.0.get() = Evaluated(thnk.invoke());
} // no other possiblity!
}
}
}
}
#[inline]
pub fn unwrap<'b>(self) -> T where T: 'b { // consumes the object to produce the value
self.force(); // evaluatate if not evealutated
match { self.0.into_inner() } {
Evaluated(v) => v,
_ => unreachable!() // previous code guarantees never not Evaluated
}
}
}
impl<'a, T: 'a> Deref for Lazy<'a, T> {
type Target = T;
#[inline]
fn deref(&self) -> &T {
self.force(); // evaluatate if not evalutated
match *unsafe { &*self.0.get() } {
Evaluated(ref v) => v,
_ => unreachable!(),
}
}
}
}
// LazyList is an immutable lazily-evaluated persistent (memoized) singly-linked list
// similar to lists in Haskell, although here only tails are lazy...
// depends on the contained type being Clone so that the LazyList can be
// extracted from the reference-counted Rc heap objects in which embedded.
pub mod lazylist {
use crate::lazy::Lazy;
use std::rc::Rc;
use std::iter::FromIterator;
use std::mem::{replace, swap};
#[derive(Clone)]
pub enum LazyList<'a, T: 'a + Clone> {
Empty,
Cons(T, RcLazyListNode<'a, T>),
}
pub use self::LazyList::Empty;
use self::LazyList::Cons;
type RcLazyListNode<'a, T> = Rc<Lazy<'a, LazyList<'a, T>>>;
// impl<'a, T:'a> !Sync for LazyList<'a, T> {}
impl<'a, T: 'a + Clone> LazyList<'a, T> {
#[inline]
pub fn singleton(v: T) -> LazyList<'a, T> {
Cons(v, Rc::new(Lazy::evaluated(Empty)))
}
#[inline]
pub fn cons<F>(v: T, cntf: F) -> LazyList<'a, T>
where F: 'a + FnOnce() -> LazyList<'a, T>
{
Cons(v, Rc::new(Lazy::new(cntf)))
}
#[inline]
pub fn head(&self) -> &T {
if let Cons(ref hd, _) = *self {
return hd;
}
panic!("LazyList::head called on an Empty LazyList!!!")
}
#[inline]
pub fn tail<'b>(&'b self) -> &'b Lazy<'a, LazyList<'a, T>> {
if let Cons(_, ref rlln) = *self {
return &*rlln;
}
panic!("LazyList::tail called on an Empty LazyList!!!")
}
#[inline]
pub fn unwrap(self) -> (T, RcLazyListNode<'a, T>) {
// consumes the object
if let Cons(hd, rlln) = self {
return (hd, rlln);
}
panic!("LazyList::unwrap called on an Empty LazyList!!!")
}
#[inline]
fn iter(&self) -> Iter<'a, T> {
Iter(self)
}
}
impl<'a, T: 'a + Clone> Iterator for LazyList<'a, T> {
type Item = T;
fn next(&mut self) -> Option<Self::Item> {
match replace(self, Empty) {
Cons(hd, rlln) => {
let mut newll = (*rlln).clone();
swap(self, &mut newll); // self now contains tail, newll contains the Empty
Some(hd)
}
_ => None,
}
}
}
pub struct Iter<'a, T: 'a + Clone>(*const LazyList<'a, T>);
impl<'a, T: 'a + Clone> Iterator for Iter<'a, T> {
type Item = &'a T;
fn next(&mut self) -> Option<Self::Item> {
unsafe {
if let LazyList::Cons(ref v, ref r) = *self.0 {
self.0 = &***r;
Some(v)
} else {
None
}
}
}
}
impl<'i, 'l, T: 'i + Clone> IntoIterator for &'l LazyList<'i, T> {
type Item = &'i T;
type IntoIter = Iter<'i, T>;
fn into_iter(self) -> Self::IntoIter {
self.iter()
}
}
impl<'a, T: 'a + Clone, > FromIterator<T> for LazyList<'a, T> {
fn from_iter<I: IntoIterator<Item = T> + 'a>(itrbl: I) -> LazyList<'a, T> {
let itr = itrbl.into_iter();
#[inline(always)]
fn next_iter<'b, R, Itr>(mut iter: Itr) -> LazyList<'b, R>
where R: 'b + Clone,
Itr: 'b + Iterator<Item = R>
{
match iter.next() {
Some(val) => LazyList::cons(val, move || next_iter(iter)),
None => Empty,
}
}
next_iter(itr)
}
}
}
Unfortunately I've exhausted ideas on how to try and fix this.
The code in the question (though not in the referenced answer, which has since been updated) relies on a soundness bug in older versions of the compiler (#18937) which has since been fixed.
It is not possible to implement FromIterator for LazyList, or indeed for any data structure, by storing the iterator inside the structure. This is because the FromIterator trait allows the implementor (Self) to outlive the iterator type (I::IntoIter). That the compiler ever accepted it was an oversight.
When copying code from the internet, be conscious of the age of the source. This code is also out of date in several other respects, notably:
it uses Rust 2015-style paths
it omits dyn on trait object types
the Invoke workaround is no longer needed, since dyn FnOnce() -> T works properly now.
I have a function that takes a collection of &T (represented by IntoIterator) with the requirement that every element is unique.
fn foo<'a, 'b, T: std::fmt::Debug, I>(elements: &'b I)
where
&'b I: IntoIterator<Item = &'a T>,
T: 'a,
'b: 'a,
I would like to also write a wrapper function which can work even if the elements are not unique, by using a HashSet to remove the duplicate elements first.
I tried the following implementation:
use std::collections::HashSet;
fn wrap<'a, 'b, T: std::fmt::Debug + Eq + std::hash::Hash, J>(elements: &'b J)
where
&'b J: IntoIterator<Item = &'a T>,
T: 'a,
'b: 'a,
{
let hashset: HashSet<&T> = elements.into_iter().into_iter().collect();
foo(&hashset);
}
playground
However, the compiler doesn't seem happy with my assumption that HashSet<&T> implements IntoIterator<Item = &'a T>:
error[E0308]: mismatched types
--> src/lib.rs:10:9
|
10 | foo(&hashset);
| ^^^^^^^^ expected type parameter, found struct `std::collections::HashSet`
|
= note: expected type `&J`
found type `&std::collections::HashSet<&T>`
= help: type parameters must be constrained to match other types
= note: for more information, visit https://doc.rust-lang.org/book/ch10-02-traits.html#traits-as-parameters
I know I could use a HashSet<T> by cloning all the input elements, but I want to avoid unnecessary copying and memory use.
If you have a &HashSet<&T> and need an iterator of &T (not &&T) that you can process multiple times, then you can use Iterator::copied to convert the iterator's &&T to a &T:
use std::{collections::HashSet, fmt::Debug, hash::Hash, marker::PhantomData};
struct Collection<T> {
item: PhantomData<T>,
}
impl<T> Collection<T>
where
T: Debug,
{
fn foo<'a, I>(elements: I) -> Self
where
I: IntoIterator<Item = &'a T> + Clone,
T: 'a,
{
for element in elements.clone() {
println!("{:?}", element);
}
for element in elements {
println!("{:?}", element);
}
Self { item: PhantomData }
}
}
impl<T> Collection<T>
where
T: Debug + Eq + Hash,
{
fn wrap<'a, I>(elements: I) -> Self
where
I: IntoIterator<Item = &'a T>,
T: 'a,
{
let set: HashSet<_> = elements.into_iter().collect();
Self::foo(set.iter().copied())
}
}
#[derive(Debug, Hash, PartialEq, Eq)]
struct Foo(i32);
fn main() {
let v = vec![Foo(1), Foo(2), Foo(4)];
Collection::<Foo>::wrap(&v);
}
See also:
Using the same iterator multiple times in Rust
Does cloning an iterator copy the entire underlying vector?
Why does cloning my custom type result in &T instead of T?
Note that the rest of this answer made the assumption that a struct named Collection<T> was a collection of values of type T. OP has clarified that this is not true.
That's not your problem, as shown by your later examples. That can be boiled down to this:
struct Collection<T>(T);
impl<T> Collection<T> {
fn new(value: &T) -> Self {
Collection(value)
}
}
You are taking a reference to a type (&T) and trying to store it where a T is required; these are different types and will generate an error. You are using PhantomData for some reason and accepting references via the iterator, but the problem is the same.
In fact, PhantomData makes the problem harder to see as you can just make up values that don't work. For example, we never have any kind of string here but we "successfully" created the struct:
use std::marker::PhantomData;
struct Collection<T>(PhantomData<T>);
impl Collection<String> {
fn new<T>(value: &T) -> Self {
Collection(PhantomData)
}
}
Ultimately, your wrap function doesn't make sense, either:
impl<T: Eq + Hash> Collection<T> {
fn wrap<I>(elements: I) -> Self
where
I: IntoIterator<Item = T>,
This is equivalent to
impl<T: Eq + Hash> Collection<T> {
fn wrap<I>(elements: I) -> Collection<T>
where
I: IntoIterator<Item = T>,
Which says that, given an iterator of elements T, you will return a collection of those elements. However, you put them in a HashMap and iterate on a reference to it, which yields &T. Thus this function signature cannot be right.
It seems most likely that you want to accept an iterator of owned values instead:
use std::{collections::HashSet, fmt::Debug, hash::Hash};
struct Collection<T> {
item: T,
}
impl<T> Collection<T> {
fn foo<I>(elements: I) -> Self
where
I: IntoIterator<Item = T>,
for<'a> &'a I: IntoIterator<Item = &'a T>,
T: Debug,
{
for element in &elements {
println!("{:?}", element);
}
for element in &elements {
println!("{:?}", element);
}
Self {
item: elements.into_iter().next().unwrap(),
}
}
}
impl<T> Collection<T>
where
T: Eq + Hash,
{
fn wrap<I>(elements: I) -> Self
where
I: IntoIterator<Item = T>,
T: Debug,
{
let s: HashSet<_> = elements.into_iter().collect();
Self::foo(s)
}
}
#[derive(Debug, Hash, PartialEq, Eq)]
struct Foo(i32);
fn main() {
let v = vec![Foo(1), Foo(2), Foo(4)];
let c = Collection::wrap(v);
println!("{:?}", c.item)
}
Here we place a trait bound on the generic iterator type directly and a second higher-ranked trait bound on a reference to the iterator. This allows us to use a reference to the iterator as an iterator itself.
See also:
How does one generically duplicate a value in Rust?
Is there any way to return a reference to a variable created in a function?
How do I write the lifetimes for references in a type constraint when one of them is a local reference?
There were a number of orthogonal issues with my code that Shepmaster pointed out, but to solve the issue of using a HashSet<&T> as an IntoIterator<Item=&T>, I found that one way to solve it is with a wrapper struct:
struct Helper<T, D: Deref<Target = T>>(HashSet<D>);
struct HelperIter<'a, T, D: Deref<Target = T>>(std::collections::hash_set::Iter<'a, D>);
impl<'a, T, D: Deref<Target = T>> Iterator for HelperIter<'a, T, D>
where
T: 'a,
{
type Item = &'a T;
fn next(&mut self) -> Option<Self::Item> {
self.0.next().map(|x| x.deref())
}
}
impl<'a, T, D: Deref<Target = T>> IntoIterator for &'a Helper<T, D> {
type Item = &'a T;
type IntoIter = HelperIter<'a, T, D>;
fn into_iter(self) -> Self::IntoIter {
HelperIter((&self.0).into_iter())
}
}
Which is used as follows:
struct Collection<T> {
item: PhantomData<T>,
}
impl<T: Debug> Collection<T> {
fn foo<I>(elements: I) -> Self
where
I: IntoIterator + Copy,
I::Item: Deref<Target = T>,
{
for element in elements {
println!("{:?}", *element);
}
for element in elements {
println!("{:?}", *element);
}
return Self { item: PhantomData };
}
}
impl<T: Debug + Eq + Hash> Collection<T> {
fn wrap<I>(elements: I) -> Self
where
I: IntoIterator + Copy,
I::Item: Deref<Target = T> + Eq + Hash,
{
let helper = Helper(elements.into_iter().collect());
Self::foo(&helper);
return Self { item: PhantomData };
}
}
fn main() {
let v = vec![Foo(1), Foo(2), Foo(4)];
Collection::<Foo>::wrap(&v);
}
I'm guessing that some of this may be more complicated than it needs to be, but I'm not sure how.
full playground
I am defining a trait called ShyAssociation that returns a lazy iterator of the keys of an associative structure. The iterator should either borrow the keys as immutable or clone them. The keys are always &'static str. All I need is an iterator that returns &'static str.
Here is the trait with my attempt at the method for the keys iterator:
use std::collections::{hash_map::Keys, HashMap};
#[derive(Clone, PartialEq, Debug)]
pub enum ShyValue {
Boolean(bool),
Integer(i64),
Rational(f64),
String(String),
Error(String),
}
pub trait ShyAssociation {
fn keys(&self) -> Keys<&'static str, ShyValue>;
}
impl ShyAssociation for HashMap<&'static str, ShyValue> {
fn keys(&self) -> Keys<&'static str, ShyValue> {
self.keys().cloned()
}
}
It won't compile.
error[E0308]: mismatched types
--> src/lib.rs:18:9
|
18 | self.keys().cloned()
| ^^^^^^^^^^^^^^^^^^^^ expected struct `std::collections::hash_map::Keys`, found struct `std::iter::Cloned`
|
= note: expected type `std::collections::hash_map::Keys<'_, &'static str, _>`
found type `std::iter::Cloned<std::collections::hash_map::Keys<'_, &str, _>>`
UPDATE:
I am attempting a variation on Alexander Huszagh's answer, but have a syntax error:
use std::collections::{hash_map::Keys, HashMap};
#[derive(Clone, PartialEq, Debug)]
pub enum ShyValue {
Boolean(bool),
Integer(i64),
Rational(f64),
String(String),
Error(String),
}
pub trait ShyAssociation<'a> {
fn keys(&self) -> Box<Iterator<Item=&'static str> + 'a>;
}
impl<'a> ShyAssociation<'a> for HashMap<&'static str, ShyValue> {
fn keys(&self) -> Box<Iterator<Item=&'static str> + 'a> {
Box::new<(Iterator<Item=&'static str> + 'a)>(self.keys().cloned())
}
}
The error message is on the "str" in the Iterator Item definition for keys:
expected `:`, found `str`
expected `:`rustc
shy_association.rs(59, 42): expected `:`
The issue is that you're manually specifying a type that doesn't match the return type. iter::Cloned<Keys<_, _>> is not the same as Keys<_, _>. A simple fix is to change your return type to iter::Cloned<Keys<&'static str, ShyValue>>.
pub trait ShyAssociation {
fn keys(&self) -> iter::Cloned<Keys<&'static str, ShyValue>>;
}
impl ShyAssociation for HashMap<&'static str, ShyValue> {
fn keys(&self) -> iter::Cloned<Keys<&'static str, ShyValue>> {
self.keys().cloned()
}
}
If would like to return a type that implements a trait (which will not work in the above example, since this is only valid for non-trait functions and methods), you may also do:
pub fn keys<'a>(hash_map: &'a HashMap<&'static str, ShyValue>) -> impl Iterator<Item = &'a str> {
hash_map.keys().cloned()
}
If you would like to use Box<dyn Iterator> so you may use it in a trait method, you may do:
pub trait ShyAssociation {
fn keys<'a>(&'a self) -> Box<(dyn Iterator<Item = &'static str> + 'a)>;
}
impl ShyAssociation for HashMap<&'static str, ShyValue> {
fn keys<'a>(&'a self) -> Box<(dyn Iterator<Item = &'static str> + 'a)> {
Box::new(self.keys().cloned())
}
}
The 'a lifetime is necessary in order to limit the lifetime of the iterator to that of the HashMap.
I have code that transforms a string reference in some way, e.g. takes the first letter
trait Tr {
fn trim_indent(self) -> Self;
}
impl<'a> Tr for &'a str {
fn trim_indent(self) -> Self {
&self[..1] // some transformation here
}
}
fn main() {
let s = "aaa".trim_indent();
println!("{}", s);
}
Now I'm trying to generalize this code for any particular type that implements AsRef<str>. My final try was
use std::ops::Deref;
trait Tr<'a> {
fn trim_indent(self) -> Deref<Target = str> + 'a + Sized;
}
impl<'a, T: AsRef<str>> Tr<'a> for T {
fn trim_indent(self) -> Deref<Target = str> + 'a + Sized {
self.as_ref()[..1] // some transformation here
}
}
fn main() {
let s = "aaa".trim_indent();
println!("{}", s);
}
I'm stuck because without Sized I get an error that type is unknown at compile time, but with Size I get an error that I cannot use marker trait explicitly.
Regardless of what type you start with, the end type of slicing a &str is always a &str so your return type needs to be a &str.
Then it's a matter of implementing the trait for references to a type so that you can tie the input and output lifetimes together:
use std::rc::Rc;
trait Tr<'a> {
fn trim_indent(self) -> &'a str;
}
impl<'a, T> Tr<'a> for &'a T
where
T: AsRef<str> + 'a,
{
fn trim_indent(self) -> &'a str {
&self.as_ref()[..1] // Take the first **byte**
}
}
fn main() {
let s: &str = "aaa";
println!("{}", s.trim_indent());
let s: Box<str> = Box::from("bbb");
println!("{}", s.trim_indent());
let s: Rc<str> = Rc::from("ccc");
println!("{}", s.trim_indent());
}
In this case, since all the types you've listed implement Deref anyway, you can just implement the trait for &str and all of the types can use it:
trait Tr {
fn trim_indent(&self) -> &str;
}
impl Tr for str {
fn trim_indent(&self) -> &str {
&self[..1]
}
}
See also:
Why is capitalizing the first letter of a string so convoluted in Rust?
I'm trying to define a trait with a method that can be implemented to either return a reference or an owned value.
Something like:
struct Type;
trait Trait {
type Value;
fn f(&self) -> Self::Value;
}
impl Trait for () {
type Value = Type;
fn f(&self) -> Self::Value {
Type
}
}
impl Trait for (Type,) {
type Value = &Type; // error[E0106]: missing lifetime specifier
fn f(&self) -> Self::Value {
&self.0
}
}
This piece of code doesn't work though, since &Type is missing a lifetime specifier. I'd want &Type to have the same lifetime as &self (i.e. fn f<'a>(&'a self) -> &'a Type), but I don't know how to express this in Rust.
I managed to find a couple of ways to make this code work, but I don't love either of them:
Adding an explicit lifetime to the trait itself:
trait Trait<'a> {
type Value;
fn f<'b>(&'b self) -> Self::Value where 'b: 'a;
}
impl<'a> Trait<'a> for () {
type Value = Type;
fn f<'b>(&'b self) -> Self::Value
where 'b: 'a
{
Type
}
}
impl<'a> Trait<'a> for (Type,) {
type Value = &'a Type;
fn f<'b>(&'b self) -> Self::Value
where 'b: 'a
{
&self.0
}
}
What I don't like of this solution is that anything using Trait needs an explicit lifetime (which I believe is not intrinsically necessary), plus the trait seems unnecessarily complicated to implement.
Returning something that might or might not be a reference - like std::borrow::Cow:
trait Trait {
type Value;
fn f<'a>(&'a self) -> Cow<'a, Self::Value>;
}
impl Trait for () {
type Value = Type;
fn f<'a>(&'a self) -> Cow<'a, Self::Value> {
Cow::Owned(Type)
}
}
impl Trait for (Type,) {
type Value = Type;
fn f<'a>(&'a self) -> Cow<'a, Self::Value> {
Cow::Borrowed(&self.0)
}
}
What I don't like of this solution is that ().f() is a Cow<_>: I'd need to call ().f().into_owned() to obtain my Type. That seems unnecessary (and might result in some negligible run-time overhead when using Trait as a trait object).
Also note that Cow is not good since it requires that Self::Value implements ToOwned (thus, practically, Clone), which is too strong of a requirement. It's anyways easy to implement an alternative to Cow without such constraints.
Are there any other solutions to this problem? What's the standard/most common/preferred one?
This could be solved using an additional associated object to choose between whether to return a type or a reference, plus some meta-programming magic.
First, some helper types:
struct Value;
struct Reference;
trait ReturnKind<'a, T: ?Sized + 'a> {
type Type: ?Sized;
}
impl<'a, T: ?Sized + 'a> ReturnKind<'a, T> for Value {
type Type = T;
}
impl<'a, T: ?Sized + 'a> ReturnKind<'a, T> for Reference {
type Type = &'a T;
}
ReturnKind is a "type-level function" which returns T when the "input" is Value, and &T for Reference.
And then the trait:
trait Trait {
type Value;
type Return: for<'a> ReturnKind<'a, Self::Value>;
fn f<'a>(&'a self) -> <Self::Return as ReturnKind<'a, Self::Value>>::Type;
}
We produce the return type by "calling" the type-level function ReturnKind.
The "input argument" Return needs to implement the trait to allow us to write <Return as ReturnKind<'a, Value>>. Although we don't know what exactly the lifetime Self will be, we could make Return bound by all possible lifetime using HRTB Return: for<'a> ReturnKind<'a, Value>.
Usage:
impl Trait for () {
type Value = f64;
type Return = Value;
fn f(&self) -> f64 {
42.0
}
}
impl Trait for (f64,) {
type Value = f64;
type Return = Reference;
fn f(&self) -> &f64 {
&self.0
}
}
fn main() {
let a: (f64,) = ( ().f(), );
let b: &f64 = a.f();
println!("{:?} {:?}", a, b);
// (42,) 42
}
Note that the above only works when the Value type has 'static lifetime. If the Value itself has a limited lifetime, this lifetime has to be known by the Trait. Since Rust doesn't support associated lifetimes yet, it has to be used like Trait<'foo>, unfortunately:
struct Value;
struct Reference;
struct ExternalReference;
trait ReturnKind<'a, 's, T: ?Sized + 'a + 's> {
type Type: ?Sized;
}
impl<'a, 's, T: ?Sized + 'a + 's> ReturnKind<'a, 's, T> for Value {
type Type = T;
}
impl<'a, 's, T: ?Sized + 'a + 's> ReturnKind<'a, 's, T> for Reference {
type Type = &'a T;
}
impl<'a, 's, T: ?Sized + 'a + 's> ReturnKind<'a, 's, T> for ExternalReference {
type Type = &'s T;
}
trait Trait<'s> {
type Value: 's;
type Return: for<'a> ReturnKind<'a, 's, Self::Value>;
fn f<'a>(&'a self) -> <Self::Return as ReturnKind<'a, 's, Self::Value>>::Type;
}
impl Trait<'static> for () {
type Value = f64;
type Return = Value;
fn f(&self) -> f64 {
42.0
}
}
impl Trait<'static> for (f64,) {
type Value = f64;
type Return = Reference;
fn f(&self) -> &f64 {
&self.0
}
}
impl<'a> Trait<'a> for (&'a f64,) {
type Value = f64;
type Return = ExternalReference;
fn f(&self) -> &'a f64 {
self.0
}
}
fn main() {
let a: (f64,) = ( ().f(), );
let b: &f64 = a.f();
let c: &f64 = (b,).f();
println!("{:?} {:?} {:?}", a, b, c);
// (42,) 42 42
}
But if having the lifetime parameter on the trait is fine, then OP already provided an easier solution:
trait Trait<'a> {
type Value;
fn f<'b>(&'b self) -> Self::Value where 'b: 'a;
}
impl<'a> Trait<'a> for () {
type Value = f64;
fn f<'b: 'a>(&'b self) -> Self::Value {
42.0
}
}
impl<'a> Trait<'a> for (f64,) {
type Value = &'a f64;
fn f<'b: 'a>(&'b self) -> Self::Value {
&self.0
}
}
impl<'a, 's> Trait<'s> for (&'a f64,) {
type Value = &'a f64;
fn f<'b: 's>(&'b self) -> Self::Value {
self.0
}
}
fn main() {
let a: (f64,) = ( ().f(), );
let b: &f64 = a.f();
let c: &f64 = (b,).f();
println!("{:?} {:?} {:?}", a, b, c);
// (42,) 42 42
}
#kennytm presented an excellent (if complex) solution; I wish to propose a much simpler alternative.
There are two possibilities to provide the lifetime name for the value:
at the trait level: trait Trait<'a> { ... }
at the method level: trait Trait { fn f<'a>(&'a self) -> ... }
The latter is not well supported by the language, and while more flexible is also quite more complicated. However, it also happens that the former is often enough; and thus without ado I present you:
trait Trait<'a> {
type Value;
fn f(self) -> Self::Value;
}
f consumes its output, this is fine if Self is an immutable reference as those are Copy.
The proof is in the pudding:
struct Type;
impl Trait<'static> for () {
type Value = Type;
fn f(self) -> Self::Value {
Type
}
}
impl<'a> Trait<'a> for &'a (Type,) {
type Value = &'a Type;
fn f(self) -> Self::Value {
&self.0
}
}
And it can be invoked without issue:
fn main(){
().f();
(Type,).f();
}
This solution is certainly not as flexible; but it's also significantly simpler.