Auto merge of #65077 - estebank:mut-trait-expected, r=nikomatsakis
Note when a mutable trait object is needed Fix https://github.com/rust-lang/rust/issues/63619, fix https://github.com/rust-lang/rust/issues/37914. CC https://github.com/rust-lang/rust/issues/64068.
This commit is contained in:
commit
8ee24f6ee0
13 changed files with 297 additions and 57 deletions
|
@ -1053,6 +1053,13 @@ impl Mutability {
|
||||||
MutImmutable => MutImmutable,
|
MutImmutable => MutImmutable,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn invert(self) -> Self {
|
||||||
|
match self {
|
||||||
|
MutMutable => MutImmutable,
|
||||||
|
MutImmutable => MutMutable,
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Copy, Clone, PartialEq, RustcEncodable, RustcDecodable, Debug, Hash, HashStable)]
|
#[derive(Copy, Clone, PartialEq, RustcEncodable, RustcDecodable, Debug, Hash, HashStable)]
|
||||||
|
|
|
@ -453,21 +453,17 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn find_similar_impl_candidates(&self,
|
fn find_similar_impl_candidates(
|
||||||
trait_ref: ty::PolyTraitRef<'tcx>)
|
&self,
|
||||||
-> Vec<ty::TraitRef<'tcx>>
|
trait_ref: ty::PolyTraitRef<'tcx>,
|
||||||
{
|
) -> Vec<ty::TraitRef<'tcx>> {
|
||||||
let simp = fast_reject::simplify_type(self.tcx,
|
let simp = fast_reject::simplify_type(self.tcx, trait_ref.skip_binder().self_ty(), true);
|
||||||
trait_ref.skip_binder().self_ty(),
|
|
||||||
true);
|
|
||||||
let all_impls = self.tcx.all_impls(trait_ref.def_id());
|
let all_impls = self.tcx.all_impls(trait_ref.def_id());
|
||||||
|
|
||||||
match simp {
|
match simp {
|
||||||
Some(simp) => all_impls.iter().filter_map(|&def_id| {
|
Some(simp) => all_impls.iter().filter_map(|&def_id| {
|
||||||
let imp = self.tcx.impl_trait_ref(def_id).unwrap();
|
let imp = self.tcx.impl_trait_ref(def_id).unwrap();
|
||||||
let imp_simp = fast_reject::simplify_type(self.tcx,
|
let imp_simp = fast_reject::simplify_type(self.tcx, imp.self_ty(), true);
|
||||||
imp.self_ty(),
|
|
||||||
true);
|
|
||||||
if let Some(imp_simp) = imp_simp {
|
if let Some(imp_simp) = imp_simp {
|
||||||
if simp != imp_simp {
|
if simp != imp_simp {
|
||||||
return None
|
return None
|
||||||
|
@ -482,10 +478,11 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn report_similar_impl_candidates(&self,
|
fn report_similar_impl_candidates(
|
||||||
impl_candidates: Vec<ty::TraitRef<'tcx>>,
|
&self,
|
||||||
err: &mut DiagnosticBuilder<'_>)
|
impl_candidates: Vec<ty::TraitRef<'tcx>>,
|
||||||
{
|
err: &mut DiagnosticBuilder<'_>,
|
||||||
|
) {
|
||||||
if impl_candidates.is_empty() {
|
if impl_candidates.is_empty() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -720,10 +717,18 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
|
||||||
// which is somewhat confusing.
|
// which is somewhat confusing.
|
||||||
err.help(&format!("consider adding a `where {}` bound",
|
err.help(&format!("consider adding a `where {}` bound",
|
||||||
trait_ref.to_predicate()));
|
trait_ref.to_predicate()));
|
||||||
} else if !have_alt_message {
|
} else {
|
||||||
// Can't show anything else useful, try to find similar impls.
|
if !have_alt_message {
|
||||||
let impl_candidates = self.find_similar_impl_candidates(trait_ref);
|
// Can't show anything else useful, try to find similar impls.
|
||||||
self.report_similar_impl_candidates(impl_candidates, &mut err);
|
let impl_candidates = self.find_similar_impl_candidates(trait_ref);
|
||||||
|
self.report_similar_impl_candidates(impl_candidates, &mut err);
|
||||||
|
}
|
||||||
|
self.suggest_change_mut(
|
||||||
|
&obligation,
|
||||||
|
&mut err,
|
||||||
|
&trait_ref,
|
||||||
|
points_at_arg,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// If this error is due to `!: Trait` not implemented but `(): Trait` is
|
// If this error is due to `!: Trait` not implemented but `(): Trait` is
|
||||||
|
@ -1081,9 +1086,11 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
|
||||||
|
|
||||||
let substs = self.tcx.mk_substs_trait(trait_type, &[]);
|
let substs = self.tcx.mk_substs_trait(trait_type, &[]);
|
||||||
let new_trait_ref = ty::TraitRef::new(trait_ref.def_id, substs);
|
let new_trait_ref = ty::TraitRef::new(trait_ref.def_id, substs);
|
||||||
let new_obligation = Obligation::new(ObligationCause::dummy(),
|
let new_obligation = Obligation::new(
|
||||||
obligation.param_env,
|
ObligationCause::dummy(),
|
||||||
new_trait_ref.to_predicate());
|
obligation.param_env,
|
||||||
|
new_trait_ref.to_predicate(),
|
||||||
|
);
|
||||||
|
|
||||||
if self.predicate_may_hold(&new_obligation) {
|
if self.predicate_may_hold(&new_obligation) {
|
||||||
let sp = self.tcx.sess.source_map()
|
let sp = self.tcx.sess.source_map()
|
||||||
|
@ -1105,6 +1112,77 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Check if the trait bound is implemented for a different mutability and note it in the
|
||||||
|
/// final error.
|
||||||
|
fn suggest_change_mut(
|
||||||
|
&self,
|
||||||
|
obligation: &PredicateObligation<'tcx>,
|
||||||
|
err: &mut DiagnosticBuilder<'tcx>,
|
||||||
|
trait_ref: &ty::Binder<ty::TraitRef<'tcx>>,
|
||||||
|
points_at_arg: bool,
|
||||||
|
) {
|
||||||
|
let span = obligation.cause.span;
|
||||||
|
if let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(span) {
|
||||||
|
let refs_number = snippet.chars()
|
||||||
|
.filter(|c| !c.is_whitespace())
|
||||||
|
.take_while(|c| *c == '&')
|
||||||
|
.count();
|
||||||
|
if let Some('\'') = snippet.chars()
|
||||||
|
.filter(|c| !c.is_whitespace())
|
||||||
|
.skip(refs_number)
|
||||||
|
.next()
|
||||||
|
{ // Do not suggest removal of borrow from type arguments.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let trait_ref = self.resolve_vars_if_possible(trait_ref);
|
||||||
|
if trait_ref.has_infer_types() {
|
||||||
|
// Do not ICE while trying to find if a reborrow would succeed on a trait with
|
||||||
|
// unresolved bindings.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if let ty::Ref(region, t_type, mutability) = trait_ref.skip_binder().self_ty().kind {
|
||||||
|
let trait_type = match mutability {
|
||||||
|
hir::Mutability::MutMutable => self.tcx.mk_imm_ref(region, t_type),
|
||||||
|
hir::Mutability::MutImmutable => self.tcx.mk_mut_ref(region, t_type),
|
||||||
|
};
|
||||||
|
|
||||||
|
let substs = self.tcx.mk_substs_trait(&trait_type, &[]);
|
||||||
|
let new_trait_ref = ty::TraitRef::new(trait_ref.skip_binder().def_id, substs);
|
||||||
|
let new_obligation = Obligation::new(
|
||||||
|
ObligationCause::dummy(),
|
||||||
|
obligation.param_env,
|
||||||
|
new_trait_ref.to_predicate(),
|
||||||
|
);
|
||||||
|
|
||||||
|
if self.evaluate_obligation_no_overflow(
|
||||||
|
&new_obligation,
|
||||||
|
).must_apply_modulo_regions() {
|
||||||
|
let sp = self.tcx.sess.source_map()
|
||||||
|
.span_take_while(span, |c| c.is_whitespace() || *c == '&');
|
||||||
|
if points_at_arg &&
|
||||||
|
mutability == hir::Mutability::MutImmutable &&
|
||||||
|
refs_number > 0
|
||||||
|
{
|
||||||
|
err.span_suggestion(
|
||||||
|
sp,
|
||||||
|
"consider changing this borrow's mutability",
|
||||||
|
"&mut ".to_string(),
|
||||||
|
Applicability::MachineApplicable,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
err.note(&format!(
|
||||||
|
"`{}` is implemented for `{:?}`, but not for `{:?}`",
|
||||||
|
trait_ref,
|
||||||
|
trait_type,
|
||||||
|
trait_ref.skip_binder().self_ty(),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn suggest_semicolon_removal(
|
fn suggest_semicolon_removal(
|
||||||
&self,
|
&self,
|
||||||
obligation: &PredicateObligation<'tcx>,
|
obligation: &PredicateObligation<'tcx>,
|
||||||
|
|
|
@ -56,7 +56,7 @@ impl<'cx, 'tcx> InferCtxt<'cx, 'tcx> {
|
||||||
// Helper function that canonicalizes and runs the query. If an
|
// Helper function that canonicalizes and runs the query. If an
|
||||||
// overflow results, we re-run it in the local context so we can
|
// overflow results, we re-run it in the local context so we can
|
||||||
// report a nice error.
|
// report a nice error.
|
||||||
fn evaluate_obligation_no_overflow(
|
crate fn evaluate_obligation_no_overflow(
|
||||||
&self,
|
&self,
|
||||||
obligation: &PredicateObligation<'tcx>,
|
obligation: &PredicateObligation<'tcx>,
|
||||||
) -> EvaluationResult {
|
) -> EvaluationResult {
|
||||||
|
|
|
@ -58,7 +58,7 @@ pub enum MethodError<'tcx> {
|
||||||
|
|
||||||
// Found a `Self: Sized` bound where `Self` is a trait object, also the caller may have
|
// Found a `Self: Sized` bound where `Self` is a trait object, also the caller may have
|
||||||
// forgotten to import a trait.
|
// forgotten to import a trait.
|
||||||
IllegalSizedBound(Vec<DefId>),
|
IllegalSizedBound(Vec<DefId>, bool),
|
||||||
|
|
||||||
// Found a match, but the return type is wrong
|
// Found a match, but the return type is wrong
|
||||||
BadReturnType,
|
BadReturnType,
|
||||||
|
@ -213,33 +213,49 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||||
segment,
|
segment,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
let mut needs_mut = false;
|
||||||
|
if let ty::Ref(region, t_type, mutability) = self_ty.kind {
|
||||||
|
let trait_type = self.tcx.mk_ref(region, ty::TypeAndMut {
|
||||||
|
ty: t_type,
|
||||||
|
mutbl: mutability.invert(),
|
||||||
|
});
|
||||||
|
match self.lookup_probe(
|
||||||
|
span,
|
||||||
|
segment.ident,
|
||||||
|
trait_type,
|
||||||
|
call_expr,
|
||||||
|
ProbeScope::TraitsInScope
|
||||||
|
) {
|
||||||
|
Ok(ref new_pick) if *new_pick != pick => {
|
||||||
|
needs_mut = true;
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if result.illegal_sized_bound {
|
if result.illegal_sized_bound {
|
||||||
// We probe again, taking all traits into account (not only those in scope).
|
// We probe again, taking all traits into account (not only those in scope).
|
||||||
let candidates =
|
let candidates = match self.lookup_probe(
|
||||||
match self.lookup_probe(span,
|
span,
|
||||||
segment.ident,
|
segment.ident,
|
||||||
self_ty,
|
self_ty,
|
||||||
call_expr,
|
call_expr,
|
||||||
ProbeScope::AllTraits) {
|
ProbeScope::AllTraits,
|
||||||
|
) {
|
||||||
// If we find a different result the caller probably forgot to import a trait.
|
// If we find a different result the caller probably forgot to import a trait.
|
||||||
Ok(ref new_pick) if *new_pick != pick => vec![new_pick.item.container.id()],
|
Ok(ref new_pick) if *new_pick != pick => vec![new_pick.item.container.id()],
|
||||||
Err(Ambiguity(ref sources)) => {
|
Err(Ambiguity(ref sources)) => sources.iter().filter_map(|source| {
|
||||||
sources.iter()
|
match *source {
|
||||||
.filter_map(|source| {
|
// Note: this cannot come from an inherent impl,
|
||||||
match *source {
|
// because the first probing succeeded.
|
||||||
// Note: this cannot come from an inherent impl,
|
ImplSource(def) => self.tcx.trait_id_of_impl(def),
|
||||||
// because the first probing succeeded.
|
TraitSource(_) => None,
|
||||||
ImplSource(def) => self.tcx.trait_id_of_impl(def),
|
|
||||||
TraitSource(_) => None,
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.collect()
|
|
||||||
}
|
}
|
||||||
_ => Vec::new(),
|
}).collect(),
|
||||||
};
|
_ => Vec::new(),
|
||||||
|
};
|
||||||
|
|
||||||
return Err(IllegalSizedBound(candidates));
|
return Err(IllegalSizedBound(candidates, needs_mut));
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(result.callee)
|
Ok(result.callee)
|
||||||
|
|
|
@ -593,22 +593,33 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||||
err.emit();
|
err.emit();
|
||||||
}
|
}
|
||||||
|
|
||||||
MethodError::IllegalSizedBound(candidates) => {
|
MethodError::IllegalSizedBound(candidates, needs_mut) => {
|
||||||
let msg = format!("the `{}` method cannot be invoked on a trait object", item_name);
|
let msg = format!("the `{}` method cannot be invoked on a trait object", item_name);
|
||||||
let mut err = self.sess().struct_span_err(span, &msg);
|
let mut err = self.sess().struct_span_err(span, &msg);
|
||||||
if !candidates.is_empty() {
|
if !candidates.is_empty() {
|
||||||
let help = format!("{an}other candidate{s} {were} found in the following \
|
let help = format!(
|
||||||
trait{s}, perhaps add a `use` for {one_of_them}:",
|
"{an}other candidate{s} {were} found in the following trait{s}, perhaps \
|
||||||
an = if candidates.len() == 1 {"an" } else { "" },
|
add a `use` for {one_of_them}:",
|
||||||
s = pluralise!(candidates.len()),
|
an = if candidates.len() == 1 {"an" } else { "" },
|
||||||
were = if candidates.len() == 1 { "was" } else { "were" },
|
s = pluralise!(candidates.len()),
|
||||||
one_of_them = if candidates.len() == 1 {
|
were = if candidates.len() == 1 { "was" } else { "were" },
|
||||||
"it"
|
one_of_them = if candidates.len() == 1 {
|
||||||
} else {
|
"it"
|
||||||
"one_of_them"
|
} else {
|
||||||
});
|
"one_of_them"
|
||||||
|
},
|
||||||
|
);
|
||||||
self.suggest_use_candidates(&mut err, help, candidates);
|
self.suggest_use_candidates(&mut err, help, candidates);
|
||||||
}
|
}
|
||||||
|
if let ty::Ref(region, t_type, mutability) = rcvr_ty.kind {
|
||||||
|
if needs_mut {
|
||||||
|
let trait_type = self.tcx.mk_ref(region, ty::TypeAndMut {
|
||||||
|
ty: t_type,
|
||||||
|
mutbl: mutability.invert(),
|
||||||
|
});
|
||||||
|
err.note(&format!("you need `{}` instead of `{}`", trait_type, rcvr_ty));
|
||||||
|
}
|
||||||
|
}
|
||||||
err.emit();
|
err.emit();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -8,6 +8,7 @@ LL | assert::<&mut i32>();
|
||||||
| ^^^^^^^^^^^^^^^^^^ `&mut i32` may not be safely transferred across an unwind boundary
|
| ^^^^^^^^^^^^^^^^^^ `&mut i32` may not be safely transferred across an unwind boundary
|
||||||
|
|
|
|
||||||
= help: the trait `std::panic::UnwindSafe` is not implemented for `&mut i32`
|
= help: the trait `std::panic::UnwindSafe` is not implemented for `&mut i32`
|
||||||
|
= note: `std::panic::UnwindSafe` is implemented for `&i32`, but not for `&mut i32`
|
||||||
|
|
||||||
error: aborting due to previous error
|
error: aborting due to previous error
|
||||||
|
|
||||||
|
|
14
src/test/ui/suggestions/imm-ref-trait-object-literal.rs
Normal file
14
src/test/ui/suggestions/imm-ref-trait-object-literal.rs
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
trait Trait {}
|
||||||
|
|
||||||
|
struct S;
|
||||||
|
|
||||||
|
impl<'a> Trait for &'a mut S {}
|
||||||
|
|
||||||
|
fn foo<X: Trait>(_: X) {}
|
||||||
|
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let s = S;
|
||||||
|
foo(&s); //~ ERROR the trait bound `&S: Trait` is not satisfied
|
||||||
|
foo(s); //~ ERROR the trait bound `S: Trait` is not satisfied
|
||||||
|
}
|
30
src/test/ui/suggestions/imm-ref-trait-object-literal.stderr
Normal file
30
src/test/ui/suggestions/imm-ref-trait-object-literal.stderr
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
error[E0277]: the trait bound `&S: Trait` is not satisfied
|
||||||
|
--> $DIR/imm-ref-trait-object-literal.rs:12:7
|
||||||
|
|
|
||||||
|
LL | fn foo<X: Trait>(_: X) {}
|
||||||
|
| --- ----- required by this bound in `foo`
|
||||||
|
...
|
||||||
|
LL | foo(&s);
|
||||||
|
| -^
|
||||||
|
| |
|
||||||
|
| the trait `Trait` is not implemented for `&S`
|
||||||
|
| help: consider changing this borrow's mutability: `&mut`
|
||||||
|
|
|
||||||
|
= help: the following implementations were found:
|
||||||
|
<&'a mut S as Trait>
|
||||||
|
|
||||||
|
error[E0277]: the trait bound `S: Trait` is not satisfied
|
||||||
|
--> $DIR/imm-ref-trait-object-literal.rs:13:7
|
||||||
|
|
|
||||||
|
LL | fn foo<X: Trait>(_: X) {}
|
||||||
|
| --- ----- required by this bound in `foo`
|
||||||
|
...
|
||||||
|
LL | foo(s);
|
||||||
|
| ^ the trait `Trait` is not implemented for `S`
|
||||||
|
|
|
||||||
|
= help: the following implementations were found:
|
||||||
|
<&'a mut S as Trait>
|
||||||
|
|
||||||
|
error: aborting due to 2 previous errors
|
||||||
|
|
||||||
|
For more information about this error, try `rustc --explain E0277`.
|
8
src/test/ui/suggestions/imm-ref-trait-object.rs
Normal file
8
src/test/ui/suggestions/imm-ref-trait-object.rs
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
fn test(t: &dyn Iterator<Item=&u64>) -> u64 {
|
||||||
|
t.min().unwrap() //~ ERROR the `min` method cannot be invoked on a trait object
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let array = [0u64];
|
||||||
|
test(&mut array.iter());
|
||||||
|
}
|
10
src/test/ui/suggestions/imm-ref-trait-object.stderr
Normal file
10
src/test/ui/suggestions/imm-ref-trait-object.stderr
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
error: the `min` method cannot be invoked on a trait object
|
||||||
|
--> $DIR/imm-ref-trait-object.rs:2:8
|
||||||
|
|
|
||||||
|
LL | t.min().unwrap()
|
||||||
|
| ^^^
|
||||||
|
|
|
||||||
|
= note: you need `&mut dyn std::iter::Iterator<Item = &u64>` instead of `&dyn std::iter::Iterator<Item = &u64>`
|
||||||
|
|
||||||
|
error: aborting due to previous error
|
||||||
|
|
|
@ -8,6 +8,7 @@ LL | foo(String::new());
|
||||||
| ^^^ the trait `std::convert::From<std::string::String>` is not implemented for `&str`
|
| ^^^ the trait `std::convert::From<std::string::String>` is not implemented for `&str`
|
||||||
|
|
|
|
||||||
= note: to coerce a `std::string::String` into a `&str`, use `&*` as a prefix
|
= note: to coerce a `std::string::String` into a `&str`, use `&*` as a prefix
|
||||||
|
= note: `std::convert::From<std::string::String>` is implemented for `&mut str`, but not for `&str`
|
||||||
= note: required because of the requirements on the impl of `std::convert::Into<&str>` for `std::string::String`
|
= note: required because of the requirements on the impl of `std::convert::Into<&str>` for `std::string::String`
|
||||||
|
|
||||||
error: aborting due to previous error
|
error: aborting due to previous error
|
||||||
|
|
23
src/test/ui/suggestions/mut-borrow-needed-by-trait.rs
Normal file
23
src/test/ui/suggestions/mut-borrow-needed-by-trait.rs
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
use std::env::args;
|
||||||
|
use std::fs::File;
|
||||||
|
use std::io::{stdout, Write, BufWriter};
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let mut args = args();
|
||||||
|
let _ = args.next();
|
||||||
|
let dest = args.next();
|
||||||
|
|
||||||
|
let h1; let h2; let h3;
|
||||||
|
|
||||||
|
let fp: &dyn Write = match dest {
|
||||||
|
Some(path) => { h1 = File::create(path).unwrap(); &h1 },
|
||||||
|
None => { h2 = stdout(); h3 = h2.lock(); &h3 }
|
||||||
|
};
|
||||||
|
|
||||||
|
let fp = BufWriter::new(fp);
|
||||||
|
//~^ ERROR the trait bound `&dyn std::io::Write: std::io::Write` is not satisfied
|
||||||
|
//~| ERROR the trait bound `&dyn std::io::Write: std::io::Write` is not satisfied
|
||||||
|
//~| ERROR the trait bound `&dyn std::io::Write: std::io::Write` is not satisfied
|
||||||
|
|
||||||
|
writeln!(fp, "hello world").unwrap(); //~ ERROR no method named `write_fmt` found for type
|
||||||
|
}
|
41
src/test/ui/suggestions/mut-borrow-needed-by-trait.stderr
Normal file
41
src/test/ui/suggestions/mut-borrow-needed-by-trait.stderr
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
error[E0277]: the trait bound `&dyn std::io::Write: std::io::Write` is not satisfied
|
||||||
|
--> $DIR/mut-borrow-needed-by-trait.rs:17:29
|
||||||
|
|
|
||||||
|
LL | let fp = BufWriter::new(fp);
|
||||||
|
| ^^ the trait `std::io::Write` is not implemented for `&dyn std::io::Write`
|
||||||
|
|
|
||||||
|
= note: `std::io::Write` is implemented for `&mut dyn std::io::Write`, but not for `&dyn std::io::Write`
|
||||||
|
= note: required by `std::io::BufWriter::<W>::new`
|
||||||
|
|
||||||
|
error[E0277]: the trait bound `&dyn std::io::Write: std::io::Write` is not satisfied
|
||||||
|
--> $DIR/mut-borrow-needed-by-trait.rs:17:14
|
||||||
|
|
|
||||||
|
LL | let fp = BufWriter::new(fp);
|
||||||
|
| ^^^^^^^^^^^^^^ the trait `std::io::Write` is not implemented for `&dyn std::io::Write`
|
||||||
|
|
|
||||||
|
= note: `std::io::Write` is implemented for `&mut dyn std::io::Write`, but not for `&dyn std::io::Write`
|
||||||
|
= note: required by `std::io::BufWriter`
|
||||||
|
|
||||||
|
error[E0277]: the trait bound `&dyn std::io::Write: std::io::Write` is not satisfied
|
||||||
|
--> $DIR/mut-borrow-needed-by-trait.rs:17:14
|
||||||
|
|
|
||||||
|
LL | let fp = BufWriter::new(fp);
|
||||||
|
| ^^^^^^^^^^^^^^^^^^ the trait `std::io::Write` is not implemented for `&dyn std::io::Write`
|
||||||
|
|
|
||||||
|
= note: `std::io::Write` is implemented for `&mut dyn std::io::Write`, but not for `&dyn std::io::Write`
|
||||||
|
= note: required by `std::io::BufWriter`
|
||||||
|
|
||||||
|
error[E0599]: no method named `write_fmt` found for type `std::io::BufWriter<&dyn std::io::Write>` in the current scope
|
||||||
|
--> $DIR/mut-borrow-needed-by-trait.rs:22:5
|
||||||
|
|
|
||||||
|
LL | writeln!(fp, "hello world").unwrap();
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ method not found in `std::io::BufWriter<&dyn std::io::Write>`
|
||||||
|
|
|
||||||
|
= note: the method `write_fmt` exists but the following trait bounds were not satisfied:
|
||||||
|
`std::io::BufWriter<&dyn std::io::Write> : std::io::Write`
|
||||||
|
= note: this error originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info)
|
||||||
|
|
||||||
|
error: aborting due to 4 previous errors
|
||||||
|
|
||||||
|
Some errors have detailed explanations: E0277, E0599.
|
||||||
|
For more information about an error, try `rustc --explain E0277`.
|
Loading…
Add table
Add a link
Reference in a new issue