Skip to content

Rust stable, fatal runtime error: stack overflow, PartialEq #57299

New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

Open
denisandroid opened this issue Jan 3, 2019 · 6 comments
Open

Rust stable, fatal runtime error: stack overflow, PartialEq #57299

denisandroid opened this issue Jan 3, 2019 · 6 comments
Labels
A-lints Area: Lints (warnings about flaws in source code) such as unused_mut. C-feature-request Category: A feature request, i.e: not implemented / a PR. L-unconditional_recursion Lint: unconditional_recursion T-compiler Relevant to the compiler team, which will review and decide on the PR/issue.

Comments

@denisandroid
Copy link

Error

cargo run
   Compiling stack_overflow v0.1.0 (/d/stack_overflow)                                                                        
    Finished dev [unoptimized + debuginfo] target(s) in 7.08s                                                                 
     Running `target/debug/stack_overflow`

thread 'main' has overflowed its stack
fatal runtime error: stack overflow
Аварийный останов (стек памяти сброшен на диск)

Rust Version

RUST STABLE:
rustc 1.31.1 (b6c32da9b 2018-12-18)

RUST NIGHTLY:
rustc 1.33.0-nightly (9eac38634 2018-12-31)

Code

use std::fmt::Write;
use std::ops::Deref;
use std::fmt;

//////The structure stores in itself the changeable reference and has to clean data in "drop" of structure.
#[derive(Debug)]
pub struct DebugSliceMutString<'a>(&'a mut String);



//Stack overflow <----
//assert_eq!(&null.my_debug(&mut string).unwrap(), "=");
impl<'a> PartialEq<str> for DebugSliceMutString<'a> {   
     fn eq(&self, other: &str) -> bool {
          self == other
     }
}



//Stack overflow <----
//assert_eq!(null.my_debug(&mut string).unwrap(), "=");
impl<'a> PartialEq<&str> for DebugSliceMutString<'a> {
     fn eq(&self, other: &&str) -> bool {
          self == other
     }
}

//If to take 'deref' that there is no overflow of a stack!!
//assert_eq!(*null.my_debug(&mut string).unwrap(), "=");
impl<'a> Deref for DebugSliceMutString<'a> {
     type Target = String;
     
     fn deref(&self) -> &String {
          self.0
     }
}

impl<'a> Drop for DebugSliceMutString<'a> {
     fn drop(&mut self) {
          self.0.clear()
     }
}



//MyTrait
pub trait MyDebug {
     fn my_debug<'a>(&self, w: &'a mut String) -> Result<DebugSliceMutString<'a>, fmt::Error>;
}

impl MyDebug for (usize, usize) {
     fn my_debug<'a>(&self, w: &'a mut String) -> Result<DebugSliceMutString<'a>, fmt::Error> {
          write!(w, "{}={}", self.0, self.1)?;

          Ok( DebugSliceMutString(w) )
     }
}

//


fn main() {
	//The buffer for an exception of frequent reallocations of memory
	let mut string = String::with_capacity(9);

	//Any other type, in this case (usize, usize) is more convenient
	let null =	(10, 20);
	

	// !!!!!!!
	//thread 'main' has overflowed its stack
	//fatal runtime error: stack overflow
	//Аварийный останов (стек памяти сброшен на диск)
	assert_eq!(	null.my_debug(&mut string).unwrap(), "=");


	// !!!!!!!
	//If to use Deref that there is no overflow of a stack!!
	//assert_eq!(	*null.my_debug(&mut string).unwrap(), "=");


	// !!!!!!!OR
	//thread 'main' has overflowed its stack
	//fatal runtime error: stack overflow
	//Аварийный останов (стек памяти сброшен на диск)
	//
	//let a = null.my_debug(&mut string).unwrap()
	//	.eq(&"=");
	//

	//
	println!("string, {}, capacity: {}", string, string.capacity());
}

Run

https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=5bacc9a73f0ae7a38100da9d196b08d0

@jonas-schievink
Copy link
Contributor

The self==other will call back into the same impl, which is unbounded recursion that then overflows the stack

@denisandroid
Copy link
Author

denisandroid commented Jan 3, 2019

"The self==other will call back into the same impl, which is unbounded recursion that then overflows the stack"

Perhaps, but 'RLS' cannot find a recursion in this case

@denisandroid
Copy link
Author

cargo check
    Checking stack_overflow v0.1.0 (/d/stack_overflow)
    Finished dev [unoptimized + debuginfo] target(s) in 9.54s

Empty..

@sinkuu
Copy link
Contributor

sinkuu commented Jan 3, 2019

unconditional_recursion lint detects only self-calling functions. Since self == other is equivalent to (&self).eq(&other), it recurses indirectly via PartialEq<&B> for &A branket impl. You can confirm this by trying (*self) == (*other), which generates the desired warning.

@estebank
Copy link
Contributor

estebank commented Jan 3, 2019

CC #45838

@estebank estebank added A-lints Area: Lints (warnings about flaws in source code) such as unused_mut. C-feature-request Category: A feature request, i.e: not implemented / a PR. labels Jan 3, 2019
@jonas-schievink jonas-schievink added the T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. label Jan 29, 2019
@Enyium
Copy link

Enyium commented Dec 26, 2023

I have the same problem.

rustc --version:

rustc 1.73.0 (cc66ad468 2023-10-03)

Minimal repro:

#![allow(dead_code, unused_mut)]

fn main() {
    let container_1 = Container::new(123);

    let mut container_2 = Container::new(123);
    container_2.set(234);

    println!("equal: {}", container_1 == container_2);
}

#[derive(Debug)]
pub struct Container<T: PartialEq> {
    old: T,
    new: Option<T>,
}

impl<T: PartialEq> Container<T> {
    pub fn new(value: T) -> Self {
        Self {
            old: value,
            new: None,
        }
    }

    pub fn set(&mut self, value: T) {
        self.new = Some(value);
    }
}

impl<T: PartialEq> PartialEq for Container<T> {
    fn eq(&self, other: &Self) -> bool {
        // Warning: "function cannot return without recursing".
        // *self == *other

        // No warning; just a stack overflow at runtime!
        &*self == &*other

        // My workaround.
        // self.new
        //     .as_ref()
        //     .unwrap_or(&self.old)
        //     .eq(other.new.as_ref().unwrap_or(&other.old))
    }
}

@tmiasko tmiasko added the L-unconditional_recursion Lint: unconditional_recursion label Apr 1, 2025
# for free to join this conversation on GitHub. Already have an account? # to comment
Labels
A-lints Area: Lints (warnings about flaws in source code) such as unused_mut. C-feature-request Category: A feature request, i.e: not implemented / a PR. L-unconditional_recursion Lint: unconditional_recursion T-compiler Relevant to the compiler team, which will review and decide on the PR/issue.
Projects
None yet
Development

No branches or pull requests

6 participants