Why should I implement "impl Encoder for &OpaqueHolder"? - rust

If I have impl X for Y and impl<T: X> X for &T, why would I still need impl X for &Y? And why do I only see this when using it in a tuple? What am I missing?
pub struct OpaqueHolder(str);
impl OpaqueHolder {
fn from_borrowed(s: &str) -> &Self {
unsafe { std::mem::transmute(s) }
}
fn as_str(&self) -> &str {
&self.0
}
}
fn make_opaque() -> &'static OpaqueHolder {
&OpaqueHolder::from_borrowed("OpaqueHolder")
}
trait Encoder {
fn as_encoded_string(&self) -> String;
fn encode(&self) -> String {
self.as_encoded_string()
}
}
impl Encoder for OpaqueHolder {
fn as_encoded_string(&self) -> String {
return self.as_str().to_string()
}
}
impl<T: Encoder> Encoder for &T {
fn as_encoded_string(&self) -> String {
(&**self).as_encoded_string()
}
}
// THIS SHOULDNT BE NECESSARY< BUT FOR SOME REASON IS... WHY?
impl Encoder for &OpaqueHolder {
fn as_encoded_string(&self) -> String {
return self.as_str().to_string()
}
}
impl<A: Encoder, B: Encoder> Encoder for (A, B) {
fn as_encoded_string(&self) -> String {
[
self.0.as_encoded_string(),
self.1.as_encoded_string(),
].concat()
}
}
fn main() {
// this works w/o the specific &OpaqueHolder
println!("{:}", make_opaque().encode());
// only shows up on the tuple
println!("{:}", (make_opaque(), make_opaque()).encode());
}

There are two things going on here:
OpaqueHolder is an unsized type since it only wraps str, which is an unsized type. A str represents some utf8-encoded sequence of bytes, but that sequence can be of any length.
Any generic type parameter like impl<T: Encoder> Encoder for &T has an implicit T: Sized constraint. Sized is a marker trait auto-implemented by the compiler on types that have a known size at compile-time.
So your generic implementation on &T doesn't work for &OpaqueHolder because OpaqueHolder isn't Sized.
To fix it, you can relax the implied Sized bound by using ?Sized:
impl<T: Encoder + ?Sized> Encoder for &T {
// ^^^^^^^^
fn as_encoded_string(&self) -> String {
(&**self).as_encoded_string()
}
}
And now it will work without needing the additional implementation (playground).

Related

Why is String -> &str different in the impl for Deref and AsRef?

Why does ops::Deref take a String to a &str differently then AsRef?
This is the impl in ops::Deref
impl ops::Deref for String {
type Target = str;
#[inline]
fn deref(&self) -> &str {
unsafe { str::from_utf8_unchecked(&self.vec) }
}
}
That's different from AsRef,
impl AsRef<str> for String {
#[inline]
fn as_ref(&self) -> &str {
self
}
}
What is the difference here between .as_ref() and .deref()
You can put a &String where &str is asked due to a type coercion rule:
&T or &mut T to &U if T implements Deref<Target = U>
T is String and U is str in this case.
impl ops::Deref for String {
type Target = str;
#[inline]
fn deref(&self) -> &str {
unsafe { str::from_utf8_unchecked(&self.vec) }
}
}
And as you can see, this rule relies on the trait Deref. And This is what #user4815162342 says as_ref() relies on deref() to do the actual work means.
For more info about type coercion, see Coercions Types.

How can I implement a method to work with &str, Box<str>, Rc<str>, etc.?

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?

How to generalize a function over two types with similar fields in different positions?

I have a function algo which works with a type S1, I also have
a type S2 which contains all of the fields of S1 plus some additional ones.
How should I modify algo to also accept S2 as input without
creating a temporary variable with type S1 and data from S2?
struct Moo1 {
f1: String,
f2: i32,
}
struct Moo2 {
f1: String,
f2: i32,
other_fields: f32,
}
struct S1 {
x: i32,
v: Vec<Moo1>,
}
struct S2 {
x: i32,
v: Vec<Moo2>,
}
//before fn algo(s: &S1)
fn algo<???>(???) {
//work with x and v (only with f1 and f2)
}
Where I'm stuck
Let's assume algo has this implementation (my real application has another implementation):
fn algo(s: &S1) {
println!("s.x: {}", s.x);
for y in &s.v {
println!("{} {}", y.f1, y.f2);
}
}
To access the field in Moo1 and Moo2 I introduce trait AsMoo, and to access x field and v I introduce trait AsS:
trait AsMoo {
fn f1(&self) -> &str;
fn f2(&self) -> i32;
}
trait AsS {
fn x(&self) -> i32;
// fn v(&self) -> ???;
}
fn algo<S: AsS>(s: &AsS) {
println!("s.x: {}", s.x());
}
I'm stuck at the implementation of the AsS::v method. I do not allocate memory to use my algo, but I need a Vec<&AsMoo> in some way.
Maybe I need to return some kind of Iterator<&AsMoo>, but have no idea how to do it and that looks complex for this problem.
Maybe I should use macros instead?
Any problem in computer science can be solved by adding another layer of indirection; at the exception of having too many such layers, of course.
Therefore, you are correct that you miss a S trait to generalize S1 and S2. In S, you can use a feature called associated type:
trait Moo {
fn f1(&self) -> &str;
fn f2(&self) -> i32;
}
trait S {
type Mooer: Moo;
fn x(&self) -> i32;
fn v(&self) -> &[Self::Mooer];
}
The bit type Mooer: Moo; says: I don't quite know what the exact type Mooer will end up being, but it'll implement the Moo trait.
This lets you write:
impl S for S1 {
type Mooer = Moo1;
fn x(&self) -> i32 { self.x }
fn v(&self) -> &[Self::Mooer] { &self.v }
}
impl S for S2 {
type Mooer = Moo2;
fn x(&self) -> i32 { self.x }
fn v(&self) -> &[Self::Mooer] { &self.v }
}
fn algo<T: S>(s: &T) {
println!("s.x: {}", s.x());
for y in s.v() {
println!("{} {}", y.f1(), y.f2());
}
}
And your generic algo knows that whatever type Mooer ends up being, it conforms to the Moo trait so the interface of Moo is available.

Is there an owned version of String::chars?

The following code does not compile:
use std::str::Chars;
struct Chunks {
remaining: Chars,
}
impl Chunks {
fn new(s: String) -> Self {
Chunks {
remaining: s.chars(),
}
}
}
The error is:
error[E0106]: missing lifetime specifier
--> src/main.rs:4:16
|
4 | remaining: Chars,
| ^^^^^ expected lifetime parameter
Chars doesn't own the characters it iterates over and it can't outlive the &str or String it was created from.
Is there an owned version of Chars that does not need a lifetime parameter or do I have to keep a Vec<char> and an index myself?
std::vec::IntoIter is an owned version of every iterator, in a sense.
use std::vec::IntoIter;
struct Chunks {
remaining: IntoIter<char>,
}
impl Chunks {
fn new(s: String) -> Self {
Chunks {
remaining: s.chars().collect::<Vec<_>>().into_iter(),
}
}
}
Playground link
Downside is additional allocation and a space overhead, but I am not aware of the iterator for your specific case.
Ouroboros
You can use the ouroboros crate to create a self-referential struct containing the String and a Chars iterator:
use ouroboros::self_referencing; // 0.4.1
use std::str::Chars;
#[self_referencing]
pub struct IntoChars {
string: String,
#[borrows(string)]
chars: Chars<'this>,
}
// All these implementations are based on what `Chars` implements itself
impl Iterator for IntoChars {
type Item = char;
#[inline]
fn next(&mut self) -> Option<Self::Item> {
self.with_mut(|me| me.chars.next())
}
#[inline]
fn count(mut self) -> usize {
self.with_mut(|me| me.chars.count())
}
#[inline]
fn size_hint(&self) -> (usize, Option<usize>) {
self.with(|me| me.chars.size_hint())
}
#[inline]
fn last(mut self) -> Option<Self::Item> {
self.with_mut(|me| me.chars.last())
}
}
impl DoubleEndedIterator for IntoChars {
#[inline]
fn next_back(&mut self) -> Option<Self::Item> {
self.with_mut(|me| me.chars.next_back())
}
}
impl std::iter::FusedIterator for IntoChars {}
// And an extension trait for convenience
trait IntoCharsExt {
fn into_chars(self) -> IntoChars;
}
impl IntoCharsExt for String {
fn into_chars(self) -> IntoChars {
IntoCharsBuilder {
string: self,
chars_builder: |s| s.chars(),
}
.build()
}
}
See also:
How can I store a Chars iterator in the same struct as the String it is iterating on?
Rental
You can use the rental crate to create a self-referential struct containing the String and a Chars iterator:
#[macro_use]
extern crate rental;
rental! {
mod into_chars {
pub use std::str::Chars;
#[rental]
pub struct IntoChars {
string: String,
chars: Chars<'string>,
}
}
}
use into_chars::IntoChars;
// All these implementations are based on what `Chars` implements itself
impl Iterator for IntoChars {
type Item = char;
#[inline]
fn next(&mut self) -> Option<Self::Item> {
self.rent_mut(|chars| chars.next())
}
#[inline]
fn count(mut self) -> usize {
self.rent_mut(|chars| chars.count())
}
#[inline]
fn size_hint(&self) -> (usize, Option<usize>) {
self.rent(|chars| chars.size_hint())
}
#[inline]
fn last(mut self) -> Option<Self::Item> {
self.rent_mut(|chars| chars.last())
}
}
impl DoubleEndedIterator for IntoChars {
#[inline]
fn next_back(&mut self) -> Option<Self::Item> {
self.rent_mut(|chars| chars.next_back())
}
}
impl std::iter::FusedIterator for IntoChars {}
// And an extension trait for convenience
trait IntoCharsExt {
fn into_chars(self) -> IntoChars;
}
impl IntoCharsExt for String {
fn into_chars(self) -> IntoChars {
IntoChars::new(self, |s| s.chars())
}
}
See also:
How can I store a Chars iterator in the same struct as the String it is iterating on?
There's also the owned-chars crate, which
provides an extension trait for String with two methods, into_chars and into_char_indices. These methods parallel String::chars and String::char_indices, but the iterators they create consume the String instead of borrowing it.
You could implement your own iterator, or wrap Chars like this (with just one small unsafe block):
// deriving Clone would be buggy. With Rc<>/Arc<> instead of Box<> it would work though.
struct OwnedChars {
// struct fields are dropped in order they are declared,
// see https://stackoverflow.com/a/41056727/1478356
// with `Chars` it probably doesn't matter, but for good style `inner`
// should be dropped before `storage`.
// 'static lifetime must not "escape" lifetime of the struct
inner: ::std::str::Chars<'static>,
// we need to box anyway to be sure the inner reference doesn't move when
// moving the storage, so we can erase the type as well.
// struct OwnedChar<S: AsRef<str>> { ..., storage: Box<S> } should work too
storage: Box<AsRef<str>>,
}
impl OwnedChars {
pub fn new<S: AsRef<str>+'static>(s: S) -> Self {
let storage = Box::new(s) as Box<AsRef<str>>;
let raw_ptr : *const str = storage.as_ref().as_ref();
let ptr : &'static str = unsafe { &*raw_ptr };
OwnedChars{
storage: storage,
inner: ptr.chars(),
}
}
pub fn as_str(&self) -> &str {
self.inner.as_str()
}
}
impl Iterator for OwnedChars {
// just `char` of course
type Item = <::std::str::Chars<'static> as Iterator>::Item;
fn next(&mut self) -> Option<Self::Item> {
self.inner.next()
}
}
impl DoubleEndedIterator for OwnedChars {
fn next_back(&mut self) -> Option<Self::Item> {
self.inner.next_back()
}
}
impl Clone for OwnedChars {
fn clone(&self) -> Self {
// need a new allocation anyway, so simply go for String, and just
// clone the remaining string
OwnedChars::new(String::from(self.inner.as_str()))
}
}
impl ::std::fmt::Debug for OwnedChars {
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
let storage : &str = self.storage.as_ref().as_ref();
f.debug_struct("OwnedChars")
.field("storage", &storage)
.field("inner", &self.inner)
.finish()
}
}
// easy access
trait StringExt {
fn owned_chars(self) -> OwnedChars;
}
impl<S: AsRef<str>+'static> StringExt for S {
fn owned_chars(self) -> OwnedChars {
OwnedChars::new(self)
}
}
See playground
As copied from How can I store a Chars iterator in the same struct as the String it is iterating on?:
use std::mem;
use std::str::Chars;
/// I believe this struct to be safe because the String is
/// heap-allocated (stable address) and will never be modified
/// (stable address). `chars` will not outlive the struct, so
/// lying about the lifetime should be fine.
///
/// TODO: What about during destruction?
/// `Chars` shouldn't have a destructor...
struct OwningChars {
_s: String,
chars: Chars<'static>,
}
impl OwningChars {
fn new(s: String) -> Self {
let chars = unsafe { mem::transmute(s.chars()) };
OwningChars { _s: s, chars }
}
}
impl Iterator for OwningChars {
type Item = char;
fn next(&mut self) -> Option<Self::Item> {
self.chars.next()
}
}
Here is a solution without unsafe.
It provides the same effect as s.chars().collect::<Vec<_>>().into_iter(), but without the allocation overhead.
struct OwnedChars {
s: String,
index: usize,
}
impl OwnedChars {
pub fn new(s: String) -> Self {
Self { s, index: 0 }
}
}
impl Iterator for OwnedChars {
type Item = char;
fn next(&mut self) -> Option<Self::Item> {
// Slice of leftover characters
let slice = &self.s[self.index..];
// Iterator over leftover characters
let mut chars = slice.chars();
// Query the next char
let next_char = chars.next()?;
// Compute the new index by looking at how many bytes are left
// after querying the next char
self.index = self.s.len() - chars.as_str().len();
// Return next char
Some(next_char)
}
}
Together with a little bit of trait magic:
trait StringExt {
fn into_chars(self) -> OwnedChars;
}
impl StringExt for String {
fn into_chars(self) -> OwnedChars {
OwnedChars::new(self)
}
}
You can do:
struct Chunks {
remaining: OwnedChars,
}
impl Chunks {
fn new(s: String) -> Self {
Chunks {
remaining: s.into_chars(),
}
}
}

Is it possible to use `impl Trait` as a function's return type in a trait definition?

Is it at all possible to define functions inside of traits as having impl Trait return types? I want to create a trait that can be implemented by multiple structs so that the new() functions of all of them returns an object that they can all be used in the same way without having to write code specific to each one.
trait A {
fn new() -> impl A;
}
However, I get the following error:
error[E0562]: `impl Trait` not allowed outside of function and inherent method return types
--> src/lib.rs:2:17
|
2 | fn new() -> impl A;
| ^^^^^^
Is this a limitation of the current implementation of impl Trait or am I using it wrong?
As trentcl mentions, you cannot currently place impl Trait in the return position of a trait method.
From RFC 1522:
impl Trait may only be written within the return type of a freestanding or inherent-impl function, not in trait definitions or any non-return type position. They may also not appear in the return type of closure traits or function pointers, unless these are themselves part of a legal return type.
Eventually, we will want to allow the feature to be used within traits [...]
For now, you must use a boxed trait object:
trait A {
fn new() -> Box<dyn A>;
}
See also:
Is it possible to have a constructor function in a trait?
Why can a trait not construct itself?
How do I return an instance of a trait from a method?
Nightly only
If you wish to use unstable nightly features, you can use existential types (RFC 2071):
// 1.67.0-nightly (2022-11-13 e631891f7ad40eac3ef5)
#![feature(type_alias_impl_trait)]
#![feature(return_position_impl_trait_in_trait)]
trait FromTheFuture {
type Iter: Iterator<Item = u8>;
fn returns_associated_type(&self) -> Self::Iter;
// Needs `return_position_impl_trait_in_trait`
fn returns_impl_trait(&self) -> impl Iterator<Item = u16>;
}
impl FromTheFuture for u8 {
// Needs `type_alias_impl_trait`
type Iter = impl Iterator<Item = u8>;
fn returns_associated_type(&self) -> Self::Iter {
std::iter::repeat(*self).take(*self as usize)
}
fn returns_impl_trait(&self) -> impl Iterator<Item = u16> {
Some((*self).into()).into_iter()
}
}
fn main() {
for v in 7.returns_associated_type() {
println!("type_alias_impl_trait: {v}");
}
for v in 7.returns_impl_trait() {
println!("return_position_impl_trait_in_trait: {v}");
}
}
If you only need to return the specific type for which the trait is currently being implemented, you may be looking for Self.
trait A {
fn new() -> Self;
}
For example, this will compile:
trait A {
fn new() -> Self;
}
struct Person;
impl A for Person {
fn new() -> Person {
Person
}
}
Or, a fuller example, demonstrating using the trait:
trait A {
fn new<S: Into<String>>(name: S) -> Self;
fn get_name(&self) -> String;
}
struct Person {
name: String
}
impl A for Person {
fn new<S: Into<String>>(name: S) -> Person {
Person { name: name.into() }
}
fn get_name(&self) -> String {
self.name.clone()
}
}
struct Pet {
name: String
}
impl A for Pet {
fn new<S: Into<String>>(name: S) -> Pet {
Pet { name: name.into() }
}
fn get_name(&self) -> String {
self.name.clone()
}
}
fn main() {
let person = Person::new("Simon");
let pet = Pet::new("Buddy");
println!("{}'s pets name is {}", get_name(&person), get_name(&pet));
}
fn get_name<T: A>(a: &T) -> String {
a.get_name()
}
Playground
As a side note.. I have used String here in favor of &str references.. to reduce the need for explicit lifetimes and potentially a loss of focus on the question at hand. I believe it's generally the convention to return a &str reference when borrowing the content and that seems appropriate here.. however I didn't want to distract from the actual example too much.
You can get something similar even in the case where it's not returning Self by using an associated type and explicitly naming the return type:
trait B {}
struct C;
impl B for C {}
trait A {
type FReturn: B;
fn f() -> Self::FReturn;
}
struct Person;
impl A for Person {
type FReturn = C;
fn f() -> C {
C
}
}
Fairly new to Rust, so may need checking.
You could parametrise over the return type. This has limits, but they're less restrictive than simply returning Self.
trait A<T> where T: A<T> {
fn new() -> T;
}
// return a Self type
struct St1;
impl A<St1> for St1 {
fn new() -> St1 { St1 }
}
// return a different type
struct St2;
impl A<St1> for St2 {
fn new() -> St1 { St1 }
}
// won't compile as u32 doesn't implement A<u32>
struct St3;
impl A<u32> for St3 {
fn new() -> u32 { 0 }
}
The limit in this case is that you can only return a type T that implements A<T>. Here, St1 implements A<St1>, so it's OK for St2 to impl A<St2>. However, it wouldn't work with, for example,
impl A<St1> for St2 ...
impl A<St2> for St1 ...
For that you'd need to restrict the types further, with e.g.
trait A<T, U> where U: A<T, U>, T: A<U, T> {
fn new() -> T;
}
but I'm struggling to get my head round this last one.

Resources